123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- import vibe.core.core;
- import vibe.http.router;
- import vibe.http.server;
- import vibe.web.web;
- import vibe.db.postgresql;
- import mir.random : unpredictableSeedOf;
- import mir.random.variable : UniformVariable;
- import mir.random.engine.xorshift : Xorshift;
- import std.conv : ConvException, to;
- import std.array;
- enum worldSize = 10000;
- enum poolSize = 64;
- PostgresClient client;
- shared static this()
- {
- import derelict.pq.pq;
- import derelict.util.exception : ShouldThrow;
- ShouldThrow myMissingSymCB( string symbolName ) { return ShouldThrow.No; }
- DerelictPQ.missingSymbolCallback = &myMissingSymCB;
- }
- void main()
- {
- runWorkerTaskDist(&runServer);
- runApplication();
- }
- void runServer()
- {
- import std.datetime : seconds;
- auto router = new URLRouter;
- router.registerWebInterface(new WebInterface);
- router.rebuild();
- auto settings = new HTTPServerSettings;
- settings.keepAliveTimeout = 20.seconds;
- settings.options |= HTTPServerOption.reusePort;
- settings.port = 8080;
- listenHTTP(settings, router);
- }
- class WebInterface {
- private {
- UniformVariable!uint _uniformVariable;
- Xorshift _gen;
- }
- this()
- {
- _gen = Xorshift(unpredictableSeedOf!uint);
- _uniformVariable = UniformVariable!uint(1, worldSize);
- }
- // GET /
- void get()
- {
- render!"index.dt";
- }
- // GET /json
- void getJson(HTTPServerResponse res)
- {
- // NOTE: the status and content type parameters are optional, but we need
- // to specify them, because the default content type is "application/json; charset=UTF8"
- res.writeJsonBody(Message("Hello, World!"), HTTPStatus.ok, "application/json");
- }
- // GET /db
- void getDB(HTTPServerResponse res)
- {
- auto conn = client.lockConnection();
- scope(exit) destroy(conn);
- int id = _uniformVariable(_gen);
- QueryParams qp;
- qp.preparedStatementName("db_prpq");
- qp.resultFormat = ValueFormat.BINARY;
- qp.argsVariadic(id);
- immutable result = conn.execPrepared(qp).rangify.front;
- auto w = WorldResponse(id, result[0].as!PGinteger);
- res.writeJsonBody(w, HTTPStatus.ok, "application/json");
- }
- // GET /queries?queries=...
- void getQueries(HTTPServerResponse res, string queries)
- {
- import std.algorithm : min, max;
- import std.uni : isNumber;
- auto conn = client.lockConnection();
- scope(exit) destroy(conn);
- // Convert the "queries" parameter to int and ignore any conversion errors
- // Note that you'd usually declare queries as int instead. However, the
- // test required to gracefully handle errors here.
- int count = 1;
- if (queries.length && isNumber(queries[0]))
- {
- try count = min(max(queries.to!int, 1), 500);
- catch (ConvException) {}
- }
- // assemble the response array
- scope data = new WorldResponse[count];
- QueryParams qp;
- qp.preparedStatementName("db_prpq");
- qp.resultFormat = ValueFormat.BINARY;
- foreach (ref w; data) {
- int id = _uniformVariable(_gen);
- qp.argsVariadic(id);
- immutable query = "SELECT randomNumber FROM world WHERE id = " ~ id.to!string;
- immutable result = conn.execPrepared(qp).rangify.front;
- w = WorldResponse(id, result[0].as!PGinteger);
- }
- // write response as JSON
- res.writeJsonBody(data, HTTPStatus.ok, "application/json");
- }
- // GET /fortunes
- void getFortunes()
- {
- import std.algorithm : map, sort;
- auto conn = client.lockConnection();
- scope(exit) destroy(conn);
- FortuneResponse[] data;
- QueryParams qp;
- qp.preparedStatementName("fortune_prpq");
- auto result = conn.execPrepared(qp).rangify;
- data = result.map!(f => FortuneResponse(f[0].as!PGinteger, f[1].as!PGtext)).array;
- data ~= FortuneResponse(0, "Additional fortune added at request time.");
- data.sort!((a, b) => a.message < b.message);
- render!("fortunes.dt", data);
- }
- // GET /updates?queries=...
- void getUpdates(HTTPServerResponse res, string queries)
- {
- import std.algorithm : min, max;
- import std.uni : isNumber;
- auto conn = client.lockConnection();
- scope(exit) destroy(conn);
- int count = 1;
- if (queries.length && isNumber(queries[0]))
- {
- try count = min(max(queries.to!int, 1), 500);
- catch (ConvException) {}
- }
- scope data = new WorldResponse[count];
- QueryParams qp;
- qp.preparedStatementName("db_prpq");
- qp.resultFormat = ValueFormat.BINARY;
- QueryParams qp_update;
- qp_update.preparedStatementName("db_update_prpq");
- qp_update.resultFormat = ValueFormat.BINARY;
- foreach (ref w; data) {
- int id = _uniformVariable(_gen);
- qp.argsVariadic(id);
- immutable query = "SELECT randomNumber FROM world WHERE id = " ~ id.to!string;
- immutable result = conn.execPrepared(qp).rangify.front;
- w = WorldResponse(id, result[0].as!PGinteger);
- // update random number
- w.randomNumber = _uniformVariable(_gen);
- qp_update.argsVariadic(w.randomNumber, id);
- // persist to DB
- conn.sendQueryPrepared(qp_update);
- }
- // write response as JSON
- res.writeJsonBody(data, HTTPStatus.ok, "application/json");
- }
- // GET /plaintext
- void getPlaintext(HTTPServerResponse res)
- {
- res.writeBody("Hello, World!", HTTPStatus.ok, "text/plain");
- }
- }
- struct Message {
- string message;
- }
- struct WorldResponse {
- int id;
- int randomNumber;
- }
- struct FortuneResponse {
- int id;
- string message;
- }
- static this()
- {
- import std.process : environment;
- auto connectionInfo = "host=tfb-database port=5432 "
- ~ "dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass";
- client = new PostgresClient(connectionInfo, poolSize, (Connection cn){
- cn.prepare("fortune_prpq", "SELECT id, message::text FROM Fortune");
- cn.prepare("db_prpq", "SELECT randomNumber FROM world WHERE id = $1");
- cn.prepare("db_update_prpq", "UPDATE world SET randomNumber = $1 WHERE id = $2");
- } );
- }
|