index.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. import {HandlebarsTemplateEngine} from '@vertx/web-templ-handlebars'
  7. const util = require('./util');
  8. const SERVER = 'vertx.js';
  9. const app = Router.router(vertx);
  10. const template = HandlebarsTemplateEngine.create(vertx);
  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. // and now delegate to the engine to render it.
  124. template.render({fortunes: fortunes}, "templates/fortunes.hbs", res => {
  125. if (res.succeeded()) {
  126. ctx.response()
  127. .putHeader("Server", SERVER)
  128. .putHeader("Date", date)
  129. .putHeader("Content-Type", "text/html; charset=UTF-8")
  130. .end(res.result());
  131. } else {
  132. ctx.fail(res.cause());
  133. }
  134. });
  135. });
  136. });
  137. /*
  138. * This test is a variation of Test #3 that exercises the ORM's persistence of objects and the database driver's
  139. * performance at running UPDATE statements or similar. The spirit of this test is to exercise a variable number of
  140. * read-then-write style database operations.
  141. */
  142. app.route("/updates").handler(ctx => {
  143. let failed = false;
  144. let queryCount = 0;
  145. let worlds = [];
  146. const queries = util.getQueries(ctx.request());
  147. for (let i = 0; i < queries; i++) {
  148. const id = util.randomWorld();
  149. const index = i;
  150. client.preparedQuery(SELECT_WORLD, Tuple.of(id), ar => {
  151. if (!failed) {
  152. if (ar.failed()) {
  153. failed = true;
  154. ctx.fail(ar.cause());
  155. return;
  156. }
  157. const row = ar.result().iterator().next();
  158. worlds[index] = {id: row.getInteger(0), randomNumber: util.randomWorld()};
  159. if (++queryCount === queries) {
  160. worlds.sort((a, b) => {
  161. return a.id - b.id;
  162. });
  163. let batch = [];
  164. worlds.forEach(world => {
  165. batch.push(Tuple.of(world.randomNumber, world.id));
  166. });
  167. client.preparedBatch(UPDATE_WORLD, batch, ar => {
  168. if (ar.failed()) {
  169. ctx.fail(ar.cause());
  170. return;
  171. }
  172. let json = [];
  173. worlds.forEach(world => {
  174. json.push({id: world.id, randomNumber: world.randomNumber});
  175. });
  176. ctx.response()
  177. .putHeader("Server", SERVER)
  178. .putHeader("Date", date)
  179. .putHeader("Content-Type", "application/json")
  180. .end(JSON.stringify(json));
  181. });
  182. }
  183. }
  184. });
  185. }
  186. });
  187. /*
  188. * This test is an exercise of the request-routing fundamentals only, designed to demonstrate the capacity of
  189. * high-performance platforms in particular. Requests will be sent using HTTP pipelining. The response payload is
  190. * still small, meaning good performance is still necessary in order to saturate the gigabit Ethernet of the test
  191. * environment.
  192. */
  193. app.get("/plaintext").handler(ctx => {
  194. ctx.response()
  195. .putHeader("Server", SERVER)
  196. .putHeader("Date", date)
  197. .putHeader("Content-Type", "text/plain")
  198. .end('Hello, World!');
  199. });
  200. vertx
  201. .createHttpServer()
  202. .requestHandler(app)
  203. .listen(8080);
  204. console.log('Server listening at: http://localhost:8080/');