lithium.cc 7.5 KB

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