lithium.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. void set_max_sql_connections_per_thread(int max)
  24. {
  25. #if TFB_MYSQL
  26. li::max_mysql_connections_per_thread = max;
  27. #elif TFB_PGSQL
  28. li::max_pgsql_connections_per_thread = max;
  29. #endif
  30. }
  31. void tune_n_sql_connections(int& nc_to_tune, std::string http_req, int port, int min, int max) {
  32. std::cout << std::endl << "Benchmark " << http_req << std::endl;
  33. auto sockets = http_benchmark_connect(512, port);
  34. float max_req_per_s = 0;
  35. int best_nconn = 2;
  36. for (int i = 0; i <= 7; i++)
  37. {
  38. int nc = min + (max - min) * i / 7;
  39. nc_to_tune = nc;
  40. // Warmup.
  41. http_benchmark(sockets, 4, 200, http_req);
  42. float req_per_s = http_benchmark(sockets, 4, 1000, http_req);
  43. std::cout << nc << " -> " << req_per_s << " req/s." << std::endl;
  44. if (req_per_s > max_req_per_s)
  45. {
  46. max_req_per_s = req_per_s;
  47. best_nconn = nc;
  48. }
  49. }
  50. http_benchmark_close(sockets);
  51. std::cout << "best: " << best_nconn << " (" << max_req_per_s << " req/s)."<< std::endl;
  52. nc_to_tune = best_nconn;
  53. }
  54. int main(int argc, char* argv[]) {
  55. if (argc != 3)
  56. {
  57. std::cerr << "Usage: " << argv[0] << " sql_host port" << std::endl;
  58. return 1;
  59. }
  60. int port = atoi(argv[2]);
  61. int nprocs = std::thread::hardware_concurrency();
  62. #if TFB_MYSQL
  63. auto sql_db =
  64. mysql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
  65. s::password = "benchmarkdbpass", s::port = 3306, s::charset = "utf8");
  66. int sql_max_connection = sql_db.connect()("SELECT @@GLOBAL.max_connections;").template read<int>() - 10;
  67. #elif TFB_PGSQL
  68. auto sql_db =
  69. pgsql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
  70. s::password = "benchmarkdbpass", s::port = 5432, s::charset = "utf8");
  71. int sql_max_connection = atoi(sql_db.connect()("SHOW max_connections;").template read<std::string>().c_str()) - 10;
  72. #endif
  73. auto fortunes = sql_orm_schema(sql_db, "Fortune").fields(
  74. s::id(s::auto_increment, s::primary_key) = int(),
  75. s::message = std::string());
  76. auto random_numbers = sql_orm_schema(sql_db, "World").fields(
  77. s::id(s::auto_increment, s::primary_key) = int(),
  78. s::randomNumber = int());
  79. #if TFB_MYSQL
  80. int db_nconn = 128/nprocs;
  81. int queries_nconn = 2;
  82. int fortunes_nconn = 128/nprocs;
  83. int updates_nconn = 1;
  84. #elif TFB_PGSQL
  85. int db_nconn = 200/nprocs;
  86. int queries_nconn = 4;
  87. int fortunes_nconn = 200/nprocs;
  88. int updates_nconn = 3;
  89. #endif
  90. http_api my_api;
  91. my_api.get("/plaintext") = [&](http_request& request, http_response& response) {
  92. response.set_header("Content-Type", "text/plain");
  93. response.write("Hello, World!");
  94. };
  95. my_api.get("/json") = [&](http_request& request, http_response& response) {
  96. response.write_json(s::message = "Hello, World!");
  97. };
  98. my_api.get("/db") = [&](http_request& request, http_response& response) {
  99. set_max_sql_connections_per_thread(db_nconn);
  100. response.write_json(random_numbers.connect(request.fiber).find_one(s::id = 1234).value());
  101. };
  102. my_api.get("/queries") = [&](http_request& request, http_response& response) {
  103. set_max_sql_connections_per_thread(queries_nconn);
  104. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  105. int N = atoi(N_str.c_str());
  106. N = std::max(1, std::min(N, 500));
  107. auto c = random_numbers.connect(request.fiber);
  108. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  109. for (int i = 0; i < N; i++)
  110. numbers[i] = c.find_one(s::id = 1 + rand() % 10000).value();
  111. response.write_json(numbers);
  112. };
  113. my_api.get("/updates") = [&](http_request& request, http_response& response) {
  114. set_max_sql_connections_per_thread(updates_nconn);
  115. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  116. int N = atoi(N_str.c_str());
  117. N = std::max(1, std::min(N, 500));
  118. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  119. {
  120. auto c = random_numbers.connect(request.fiber);
  121. auto& raw_c = c.backend_connection();
  122. #if TFB_MYSQL
  123. raw_c("START TRANSACTION");
  124. #endif
  125. for (int i = 0; i < N; i++)
  126. {
  127. numbers[i] = c.find_one(s::id = 1 + rand() % 10000).value();
  128. numbers[i].randomNumber = 1 + rand() % 10000;
  129. }
  130. std::sort(numbers.begin(), numbers.end(), [] (auto a, auto b) { return a.id < b.id; });
  131. c.bulk_update(numbers);
  132. #if TFB_MYSQL
  133. raw_c("COMMIT");
  134. #endif
  135. }
  136. response.write_json(numbers);
  137. };
  138. my_api.get("/fortunes") = [&](http_request& request, http_response& response) {
  139. set_max_sql_connections_per_thread(fortunes_nconn);
  140. typedef decltype(fortunes.all_fields()) fortune;
  141. std::vector<fortune> table;
  142. auto c = fortunes.connect(request.fiber);
  143. c.forall([&] (auto f) { table.emplace_back(f); });
  144. table.emplace_back(0, "Additional fortune added at request time.");
  145. std::sort(table.begin(), table.end(),
  146. [] (const fortune& a, const fortune& b) { return a.message < b.message; });
  147. char b[100000];
  148. li::output_buffer ss(b, sizeof(b));
  149. ss << "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
  150. for(auto& f : table)
  151. {
  152. ss << "<tr><td>" << f.id << "</td><td>";
  153. escape_html_entities(ss, f.message);
  154. ss << "</td></tr>";
  155. }
  156. ss << "</table></body></html>";
  157. response.set_header("Content-Type", "text/html; charset=utf-8");
  158. response.write(ss.to_string_view());
  159. };
  160. // Start the server for the Techempower benchmark.
  161. http_serve(my_api, port, s::nthreads = nprocs);
  162. return 0;
  163. }