app.rb 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. # frozen_string_literal: true
  2. require 'agoo'
  3. require 'connection_pool'
  4. require 'oj'
  5. require 'pg'
  6. require 'rack'
  7. $pool = ConnectionPool.new(size: 1, timeout: 5) do
  8. PG::Connection.new({
  9. dbname: 'hello_world',
  10. host: 'tfb-database',
  11. user: 'benchmarkdbuser',
  12. password: 'benchmarkdbpass'
  13. })
  14. end
  15. QUERY_RANGE = (1..10_000).freeze
  16. ALL_IDS = QUERY_RANGE.to_a
  17. QUERIES_MIN = 1
  18. QUERIES_MAX = 500
  19. CONTENT_TYPE = 'Content-Type'
  20. CONTENT_LENGTH = 'Content-Length'
  21. DATE = 'Date'
  22. SERVER = 'Server'
  23. SERVER_STRING = 'Agoo'
  24. JSON_TYPE = 'application/json'
  25. HTML_TYPE = 'text/html; charset=utf-8'
  26. PLAINTEXT_TYPE = 'text/plain'
  27. class BaseHandler
  28. def self.extract_queries_param(request = nil)
  29. queries = Rack::Utils.parse_query(request['QUERY_STRING'])['queries'].to_i rescue 1
  30. queries.clamp(QUERIES_MIN, QUERIES_MAX)
  31. end
  32. def self.get_one_random_number
  33. Random.rand(QUERY_RANGE)
  34. end
  35. def self.get_one_record(id = get_one_random_number)
  36. $pool.with do |conn|
  37. conn.exec_params(<<-SQL, [id]).first
  38. SELECT * FROM world WHERE id = $1
  39. SQL
  40. end
  41. end
  42. def self.html_response(str = '')
  43. [
  44. 200,
  45. {
  46. CONTENT_TYPE => HTML_TYPE,
  47. DATE => Time.now.httpdate,
  48. SERVER => SERVER_STRING
  49. },
  50. [str]
  51. ]
  52. end
  53. def self.json_response(obj = {})
  54. [
  55. 200,
  56. {
  57. CONTENT_TYPE => JSON_TYPE,
  58. DATE => Time.now.httpdate,
  59. SERVER => SERVER_STRING
  60. },
  61. [Oj.dump(obj, { :mode => :strict })]
  62. ]
  63. end
  64. def self.plain_response(str = '')
  65. [
  66. 200,
  67. {
  68. CONTENT_TYPE => PLAINTEXT_TYPE,
  69. DATE => Time.now.httpdate,
  70. SERVER => SERVER_STRING
  71. },
  72. [str]
  73. ]
  74. end
  75. end
  76. class PlaintextHandler < BaseHandler
  77. def self.call(_req)
  78. plain_response('Hello, World!')
  79. end
  80. def static?
  81. true
  82. end
  83. end
  84. class JsonHandler < BaseHandler
  85. def self.call(_req)
  86. json_response({ :message => "Hello, World!" })
  87. end
  88. def static?
  89. true
  90. end
  91. end
  92. class DbHandler < BaseHandler
  93. def self.call(_req)
  94. json_response(get_one_record)
  95. end
  96. end
  97. class FortunesHandler < BaseHandler
  98. def self.call(_req)
  99. f_1 = $pool.with do |conn|
  100. conn.exec(<<-SQL)
  101. SELECT id, message FROM fortune
  102. SQL
  103. end
  104. f_2 = f_1.map(&:to_h).
  105. append({ 'id' => '0', 'message' => 'Additional fortune added at request time.' }).
  106. sort_by { |item| item['message'] }.
  107. map { |f| "<tr><td>#{ f['id'] }</td><td>#{ Rack::Utils.escape_html(f['message']) }</td></tr>" }.
  108. join
  109. html_response(<<-HTML)
  110. <!DOCTYPE html>
  111. <html>
  112. <head>
  113. <title>Fortunes</title>
  114. </head>
  115. <body>
  116. <table>
  117. <tr>
  118. <th>id</th>
  119. <th>message</th>
  120. </tr>
  121. #{ f_2 }
  122. </table>
  123. </body>
  124. </html>
  125. HTML
  126. end
  127. end
  128. class QueriesHandler < BaseHandler
  129. def self.call(req)
  130. queries = extract_queries_param req
  131. records = ALL_IDS.sample(queries).map do |id|
  132. get_one_record(id)
  133. end
  134. json_response(records)
  135. end
  136. end
  137. class UpdatesHandler < BaseHandler
  138. def self.call(req)
  139. queries = extract_queries_param req
  140. records = ALL_IDS.sample(queries).sort.map do |id|
  141. world = get_one_record(id)
  142. world['randomnumber'] = get_one_random_number
  143. world
  144. end
  145. sql_values =
  146. records.
  147. map { |r|
  148. "(#{ r['id'] }, #{ r['randomnumber'] })"
  149. }.join(', ')
  150. $pool.with do |conn|
  151. conn.exec(<<-SQL)
  152. UPDATE world AS ori
  153. SET randomnumber = new.randomnumber
  154. FROM (VALUES #{ sql_values }) AS new (id, randomnumber)
  155. WHERE ori.id = new.id
  156. SQL
  157. end
  158. json_response(records)
  159. end
  160. end
  161. Agoo::Log.configure({
  162. classic: true,
  163. colorize: true,
  164. console: true,
  165. dir: '',
  166. states: {
  167. DEBUG: false,
  168. INFO: false,
  169. connect: false,
  170. eval: false,
  171. push: false,
  172. request: false,
  173. response: false
  174. }
  175. })
  176. worker_count = 4
  177. worker_count = ENV['AGOO_WORKER_COUNT'].to_i if ENV.key?('AGOO_WORKER_COUNT')
  178. Agoo::Server.init(8080, '.', thread_count: 0, worker_count: worker_count)
  179. Agoo::Server.handle(:GET, '/plaintext', PlaintextHandler)
  180. Agoo::Server.handle(:GET, '/json', JsonHandler)
  181. Agoo::Server.handle(:GET, '/db', DbHandler)
  182. Agoo::Server.handle(:GET, '/fortunes', FortunesHandler)
  183. Agoo::Server.handle(:GET, '/queries', QueriesHandler)
  184. Agoo::Server.handle(:GET, '/updates', UpdatesHandler)
  185. Agoo::Server.start