postgresql.d 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import vibe.core.core;
  2. import vibe.http.router;
  3. import vibe.http.server;
  4. import vibe.web.web;
  5. import vibe.db.postgresql;
  6. import mir.random : unpredictableSeedOf;
  7. import mir.random.variable : UniformVariable;
  8. import mir.random.engine.xorshift : Xorshift;
  9. import std.conv : ConvException, to;
  10. import std.array;
  11. enum worldSize = 10000;
  12. enum poolSize = 256;
  13. PostgresClient client;
  14. shared static this()
  15. {
  16. import derelict.pq.pq;
  17. import derelict.util.exception : ShouldThrow;
  18. ShouldThrow myMissingSymCB( string symbolName ) { return ShouldThrow.No; }
  19. DerelictPQ.missingSymbolCallback = &myMissingSymCB;
  20. }
  21. void main()
  22. {
  23. runWorkerTaskDist(&runServer);
  24. runApplication();
  25. }
  26. void runServer()
  27. {
  28. auto router = new URLRouter;
  29. router.registerWebInterface(new WebInterface);
  30. router.rebuild();
  31. auto settings = new HTTPServerSettings;
  32. settings.options |= HTTPServerOption.reusePort;
  33. settings.port = 8080;
  34. listenHTTP(settings, router);
  35. }
  36. class WebInterface {
  37. private {
  38. UniformVariable!uint _uniformVariable;
  39. Xorshift _gen;
  40. }
  41. this()
  42. {
  43. _gen = Xorshift(unpredictableSeedOf!uint);
  44. _uniformVariable = UniformVariable!uint(1, worldSize);
  45. }
  46. // GET /
  47. void get()
  48. {
  49. render!"index.dt";
  50. }
  51. // GET /json
  52. void getJson(HTTPServerResponse res)
  53. {
  54. // NOTE: the status and content type parameters are optional, but we need
  55. // to specify them, because the default content type is "application/json; charset=UTF8"
  56. res.writeJsonBody(Message("Hello, World!"), HTTPStatus.ok, "application/json");
  57. }
  58. // GET /db
  59. void getDB(HTTPServerResponse res)
  60. {
  61. auto conn = client.lockConnection();
  62. scope(exit) destroy(conn);
  63. immutable id = _uniformVariable(_gen);
  64. immutable query = "SELECT randomNumber FROM world WHERE id = " ~ id.to!string;
  65. immutable result = conn.execStatement(query, ValueFormat.BINARY).rangify.front;
  66. auto w = WorldResponse(id, result[0].as!PGinteger);
  67. res.writeJsonBody(w, HTTPStatus.ok, "application/json");
  68. }
  69. // GET /queries?queries=...
  70. void getQueries(HTTPServerResponse res, string queries)
  71. {
  72. import std.algorithm : min, max;
  73. auto conn = client.lockConnection();
  74. scope(exit) destroy(conn);
  75. // Convert the "queries" parameter to int and ignore any conversion errors
  76. // Note that you'd usually declare queries as int instead. However, the
  77. // test required to gracefully handle errors here.
  78. int count = 1;
  79. try count = min(max(queries.to!int, 1), 500);
  80. catch (ConvException) {}
  81. // assemble the response array
  82. scope data = new WorldResponse[count];
  83. foreach (ref w; data) {
  84. immutable id = _uniformVariable(_gen);
  85. immutable query = "SELECT randomNumber FROM world WHERE id = " ~ id.to!string;
  86. immutable result = conn.execStatement(query, ValueFormat.BINARY).rangify.front;
  87. w = WorldResponse(id, result[0].as!PGinteger);
  88. }
  89. // write response as JSON
  90. res.writeJsonBody(data, HTTPStatus.ok, "application/json");
  91. }
  92. // GET /fortunes
  93. void getFortunes()
  94. {
  95. import std.algorithm : map, sort;
  96. auto conn = client.lockConnection();
  97. scope(exit) destroy(conn);
  98. FortuneResponse[] data;
  99. immutable query = "SELECT id, message::text FROM Fortune";
  100. auto result = conn.execStatement(query, ValueFormat.BINARY).rangify;
  101. data = result.map!(f => FortuneResponse(f[0].as!PGinteger, f[1].as!PGtext)).array;
  102. data ~= FortuneResponse(0, "Additional fortune added at request time.");
  103. data.sort!((a, b) => a.message < b.message);
  104. render!("fortunes.dt", data);
  105. }
  106. // GET /updates?queries=...
  107. void getUpdates(HTTPServerResponse res, string queries)
  108. {
  109. import std.algorithm : min, max;
  110. auto conn = client.lockConnection();
  111. scope(exit) destroy(conn);
  112. int count = 1;
  113. try count = min(max(queries.to!int, 1), 500);
  114. catch (ConvException e) {}
  115. scope data = new WorldResponse[count];
  116. foreach (ref w; data) {
  117. immutable id = _uniformVariable(_gen);
  118. immutable sid = id.to!string;
  119. immutable query = "SELECT randomNumber FROM world WHERE id = " ~ sid;
  120. immutable result = conn.execStatement(query, ValueFormat.BINARY).rangify.front;
  121. w = WorldResponse(id, result[0].as!PGinteger);
  122. // update random number
  123. w.randomNumber = _uniformVariable(_gen);
  124. // persist to DB
  125. conn.execStatement("UPDATE world SET randomNumber = " ~ w.randomNumber.to!string ~ " WHERE id = " ~ sid);
  126. }
  127. // write response as JSON
  128. res.writeJsonBody(data, HTTPStatus.ok, "application/json");
  129. }
  130. // GET /plaintext
  131. void getPlaintext(HTTPServerResponse res)
  132. {
  133. res.writeBody("Hello, World!", HTTPStatus.ok, "text/plain");
  134. }
  135. }
  136. struct Message {
  137. string message;
  138. }
  139. struct WorldResponse {
  140. int id;
  141. int randomNumber;
  142. }
  143. struct FortuneResponse {
  144. int id;
  145. string message;
  146. }
  147. static this()
  148. {
  149. import std.process : environment;
  150. auto connectionInfo = "host=tfb-database port=5432 "
  151. ~ "dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass";
  152. client = new PostgresClient(connectionInfo, poolSize);
  153. }