lithium.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. int main(int argc, char* argv[]) {
  32. if (argc != 3)
  33. {
  34. std::cerr << "Usage: " << argv[0] << " sql_host port" << std::endl;
  35. return 1;
  36. }
  37. int port = atoi(argv[2]);
  38. int nprocs = std::thread::hardware_concurrency();
  39. #if TFB_MYSQL
  40. auto sql_db =
  41. mysql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
  42. s::password = "benchmarkdbpass", s::port = 3306, s::charset = "utf8");
  43. int mysql_max_connection = 256;
  44. li::max_mysql_connections_per_thread = (mysql_max_connection / nprocs);
  45. std::cout << "Using " << li::max_mysql_connections_per_thread << " connections per thread. " << nprocs << " threads." << std::endl;
  46. #elif TFB_PGSQL
  47. auto sql_db =
  48. pgsql_database(s::host = argv[1], s::database = "hello_world", s::user = "benchmarkdbuser",
  49. s::password = "benchmarkdbpass", s::port = 5432, s::charset = "utf8");
  50. int pgsql_max_connection = 256;
  51. li::max_pgsql_connections_per_thread = (pgsql_max_connection / nprocs);
  52. std::cout << "Using " << li::max_pgsql_connections_per_thread << " connections per thread. " << nprocs << " threads." << std::endl;
  53. #endif
  54. auto fortunes = sql_orm_schema(sql_db, "Fortune").fields(
  55. s::id(s::auto_increment, s::primary_key) = int(),
  56. s::message = std::string());
  57. auto random_numbers = sql_orm_schema(sql_db, "World").fields(
  58. s::id(s::auto_increment, s::primary_key) = int(),
  59. s::randomNumber = int());
  60. http_api my_api;
  61. my_api.get("/plaintext") = [&](http_request& request, http_response& response) {
  62. response.set_header("Content-Type", "text/plain");
  63. response.write("Hello, World!");
  64. };
  65. my_api.get("/json") = [&](http_request& request, http_response& response) {
  66. response.write_json(s::message = "Hello, World!");
  67. };
  68. my_api.get("/db") = [&](http_request& request, http_response& response) {
  69. set_max_sql_connections_per_thread(1024 / nprocs);
  70. response.write_json(random_numbers.connect(request.yield).find_one(s::id = 1234).value());
  71. };
  72. my_api.get("/queries") = [&](http_request& request, http_response& response) {
  73. set_max_sql_connections_per_thread(2);
  74. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  75. int N = atoi(N_str.c_str());
  76. N = std::max(1, std::min(N, 500));
  77. auto c = random_numbers.connect(request.yield);
  78. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  79. for (int i = 0; i < N; i++)
  80. numbers[i] = c.find_one(s::id = 1 + rand() % 10000).value();
  81. response.write_json(numbers);
  82. };
  83. my_api.get("/updates") = [&](http_request& request, http_response& response) {
  84. set_max_sql_connections_per_thread(2);
  85. std::string N_str = request.get_parameters(s::N = std::optional<std::string>()).N.value_or("1");
  86. int N = atoi(N_str.c_str());
  87. N = std::max(1, std::min(N, 500));
  88. std::vector<decltype(random_numbers.all_fields())> numbers(N);
  89. {
  90. auto c = random_numbers.connect(request.yield);
  91. auto& raw_c = c.backend_connection();
  92. #if TFB_MYSQL
  93. raw_c("START TRANSACTION");
  94. #endif
  95. for (int i = 0; i < N; i++)
  96. {
  97. numbers[i] = c.find_one(s::id = 1 + rand() % 10000).value();
  98. numbers[i].randomNumber = 1 + rand() % 10000;
  99. }
  100. std::sort(numbers.begin(), numbers.end(), [] (auto a, auto b) { return a.id < b.id; });
  101. #if TFB_MYSQL
  102. for (int i = 0; i < N; i++)
  103. c.update(numbers[i]);
  104. raw_c("COMMIT");
  105. #elif TFB_PGSQL
  106. raw_c.cached_statement
  107. ([N] {
  108. std::ostringstream ss;
  109. ss << "UPDATE World SET randomNumber=tmp.randomNumber FROM (VALUES ";
  110. for (int i = 0; i < N; i++)
  111. ss << "($" << i*2+1 << "::integer, $" << i*2+2 << "::integer) "<< (i == N-1 ? "": ",");
  112. ss << ") AS tmp(id, randomNumber) WHERE tmp.id = World.id";
  113. return ss.str();
  114. }, N)(numbers);
  115. #endif
  116. }
  117. response.write_json(numbers);
  118. };
  119. my_api.get("/fortunes") = [&](http_request& request, http_response& response) {
  120. set_max_sql_connections_per_thread(1024 / nprocs);
  121. typedef decltype(fortunes.all_fields()) fortune;
  122. std::vector<fortune> table;
  123. auto c = fortunes.connect(request.yield);
  124. c.forall([&] (auto f) { table.emplace_back(f); });
  125. table.emplace_back(0, "Additional fortune added at request time.");
  126. std::sort(table.begin(), table.end(),
  127. [] (const fortune& a, const fortune& b) { return a.message < b.message; });
  128. char b[100000];
  129. li::output_buffer ss(b, sizeof(b));
  130. ss << "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
  131. for(auto& f : table)
  132. {
  133. ss << "<tr><td>" << f.id << "</td><td>";
  134. escape_html_entities(ss, f.message);
  135. ss << "</td></tr>";
  136. }
  137. ss << "</table></body></html>";
  138. response.set_header("Content-Type", "text/html; charset=utf-8");
  139. response.write(ss.to_string_view());
  140. };
  141. http_serve(my_api, port, s::nthreads = nprocs);
  142. return 0;
  143. }