index.js 7.4 KB

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