index.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /// <reference types="@vertx/core/runtime" />
  2. // @ts-check
  3. import {Router} from '@vertx/web';
  4. import {PgClient, Tuple} from '@reactiverse/reactive-pg-client';
  5. import {PgPoolOptions} from '@reactiverse/reactive-pg-client/options';
  6. const util = require('./util');
  7. const HandlebarsTemplateEngine = Java.type('io.vertx.ext.web.templ.HandlebarsTemplateEngine');
  8. const SERVER = 'vertx.js';
  9. const app = Router.router(vertx);
  10. const template = HandlebarsTemplateEngine.create();
  11. let date = new Date().toString();
  12. vertx.setPeriodic(1000, t => date = new Date().toUTCString());
  13. /*
  14. * This test exercises the framework fundamentals including keep-alive support, request routing, request header
  15. * parsing, object instantiation, JSON serialization, response header generation, and request count throughput.
  16. */
  17. app.get("/json").handler(ctx => {
  18. ctx.response()
  19. .putHeader("Server", SERVER)
  20. .putHeader("Date", date)
  21. .putHeader("Content-Type", "application/json")
  22. .end(JSON.stringify({message: 'Hello, World!'}));
  23. });
  24. const UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2";
  25. const SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1";
  26. const SELECT_FORTUNE = "SELECT id, message from FORTUNE";
  27. let client = PgClient.pool(
  28. vertx,
  29. new PgPoolOptions()
  30. .setCachePreparedStatements(true)
  31. .setMaxSize(1)
  32. .setHost('tfb-database')
  33. .setUser('benchmarkdbuser')
  34. .setPassword('benchmarkdbpass')
  35. .setDatabase('hello_world'));
  36. /*
  37. * This test exercises the framework's object-relational mapper (ORM), random number generator, database driver,
  38. * and database connection pool.
  39. */
  40. app.get("/db").handler(ctx => {
  41. client.preparedQuery(SELECT_WORLD, Tuple.of(util.randomWorld()), res => {
  42. if (res.succeeded()) {
  43. let resultSet = res.result().iterator();
  44. if (!resultSet.hasNext()) {
  45. ctx.fail(404);
  46. return;
  47. }
  48. let row = resultSet.next();
  49. ctx.response()
  50. .putHeader("Server", SERVER)
  51. .putHeader("Date", date)
  52. .putHeader("Content-Type", "application/json")
  53. .end(JSON.stringify({id: row.getInteger(0), randomNumber: row.getInteger(1)}));
  54. } else {
  55. ctx.fail(res.cause());
  56. }
  57. })
  58. });
  59. /*
  60. * This test is a variation of Test #2 and also uses the World table. Multiple rows are fetched to more dramatically
  61. * punish the database driver and connection pool. At the highest queries-per-request tested (20), this test
  62. * demonstrates all frameworks' convergence toward zero requests-per-second as database activity increases.
  63. */
  64. app.get("/queries").handler(ctx => {
  65. let failed = false;
  66. let worlds = [];
  67. const queries = util.getQueries(ctx.request());
  68. for (let i = 0; i < queries; i++) {
  69. client.preparedQuery(SELECT_WORLD, Tuple.of(util.randomWorld()), ar => {
  70. if (!failed) {
  71. if (ar.failed()) {
  72. failed = true;
  73. ctx.fail(ar.cause());
  74. return;
  75. }
  76. // we need a final reference
  77. const row = ar.result().iterator().next();
  78. worlds.push({id: row.getInteger(0), randomNumber: row.getInteger(1)});
  79. // stop condition
  80. if (worlds.length === queries) {
  81. ctx.response()
  82. .putHeader("Server", SERVER)
  83. .putHeader("Date", date)
  84. .putHeader("Content-Type", "application/json")
  85. .end(JSON.stringify(worlds));
  86. }
  87. }
  88. });
  89. }
  90. });
  91. /*
  92. * This test exercises the ORM, database connectivity, dynamic-size collections, sorting, server-side templates,
  93. * XSS countermeasures, and character encoding.
  94. */
  95. app.get("/fortunes").handler(ctx => {
  96. client.preparedQuery(SELECT_FORTUNE, ar => {
  97. if (ar.failed()) {
  98. ctx.fail(ar.cause());
  99. return;
  100. }
  101. let fortunes = [];
  102. let resultSet = ar.result().iterator();
  103. if (!resultSet.hasNext()) {
  104. ctx.fail(404);
  105. return;
  106. }
  107. while (resultSet.hasNext()) {
  108. let row = resultSet.next();
  109. fortunes.push({id: row.getInteger(0), message: row.getString(1)});
  110. }
  111. fortunes.push({id: 0, message: "Additional fortune added at request time."});
  112. fortunes.sort((a, b) => {
  113. let messageA = a.message;
  114. let messageB = b.message;
  115. if (messageA < messageB) {
  116. return -1;
  117. }
  118. if (messageA > messageB) {
  119. return 1;
  120. }
  121. return 0;
  122. });
  123. ctx.put("fortunes", fortunes);
  124. // and now delegate to the engine to render it.
  125. template.render(ctx, "templates", "/fortunes.hbs", res => {
  126. if (res.succeeded()) {
  127. ctx.response()
  128. .putHeader("Server", SERVER)
  129. .putHeader("Date", date)
  130. .putHeader("Content-Type", "text/html; charset=UTF-8")
  131. .end(res.result());
  132. } else {
  133. ctx.fail(res.cause());
  134. }
  135. });
  136. });
  137. });
  138. /*
  139. * This test is a variation of Test #3 that exercises the ORM's persistence of objects and the database driver's
  140. * performance at running UPDATE statements or similar. The spirit of this test is to exercise a variable number of
  141. * read-then-write style database operations.
  142. */
  143. app.route("/updates").handler(ctx => {
  144. let failed = false;
  145. let queryCount = 0;
  146. let worlds = [];
  147. const queries = util.getQueries(ctx.request());
  148. for (let i = 0; i < queries; i++) {
  149. const id = util.randomWorld();
  150. const index = i;
  151. client.preparedQuery(SELECT_WORLD, Tuple.of(id), ar => {
  152. if (!failed) {
  153. if (ar.failed()) {
  154. failed = true;
  155. ctx.fail(ar.cause());
  156. return;
  157. }
  158. const row = ar.result().iterator().next();
  159. worlds[index] = {id: row.getInteger(0), randomNumber: util.randomWorld()};
  160. if (++queryCount === queries) {
  161. worlds.sort((a, b) => {
  162. return a.id - b.id;
  163. });
  164. let batch = [];
  165. worlds.forEach(world => {
  166. batch.push(Tuple.of(world.randomNumber, world.id));
  167. });
  168. client.preparedBatch(UPDATE_WORLD, batch, ar => {
  169. if (ar.failed()) {
  170. ctx.fail(ar.cause());
  171. return;
  172. }
  173. let json = [];
  174. worlds.forEach(world => {
  175. json.push({id: world.id, randomNumber: world.randomNumber});
  176. });
  177. ctx.response()
  178. .putHeader("Server", SERVER)
  179. .putHeader("Date", date)
  180. .putHeader("Content-Type", "application/json")
  181. .end(JSON.stringify(json));
  182. });
  183. }
  184. }
  185. });
  186. }
  187. });
  188. /*
  189. * This test is an exercise of the request-routing fundamentals only, designed to demonstrate the capacity of
  190. * high-performance platforms in particular. Requests will be sent using HTTP pipelining. The response payload is
  191. * still small, meaning good performance is still necessary in order to saturate the gigabit Ethernet of the test
  192. * environment.
  193. */
  194. app.get("/plaintext").handler(ctx => {
  195. ctx.response()
  196. .putHeader("Server", SERVER)
  197. .putHeader("Date", date)
  198. .putHeader("Content-Type", "text/plain")
  199. .end('Hello, World!');
  200. });
  201. vertx
  202. .createHttpServer()
  203. .requestHandler(req => app.accept(req))
  204. .listen(8080);
  205. console.log('Server listening at: http://localhost:8080/');