Browse Source

Photon http fortunes (#10239)

* Add Fortunes endpoint

* Forgot the template file

* Minor fixes to pass the verification
Dmitry Olshansky 1 month ago
parent
commit
8218168bcc

+ 1 - 0
frameworks/D/photon-http/benchmark_config.json

@@ -3,6 +3,7 @@
   "tests": [
   "tests": [
     {
     {
       "default": {
       "default": {
+        "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
         "update_url": "/updates?queries=",
         "query_url": "/queries?queries=",
         "query_url": "/queries?queries=",
         "db_url": "/db",
         "db_url": "/db",

+ 3 - 1
frameworks/D/photon-http/dub.json

@@ -9,8 +9,10 @@
 		"mir-ion": "~>2.3.4",
 		"mir-ion": "~>2.3.4",
 		"xbuf" : "~>0.2.1",
 		"xbuf" : "~>0.2.1",
 		"photon": "~>0.18.10",
 		"photon": "~>0.18.10",
-		"photon-http": "~>0.6.8"
+		"photon-http": "~>0.6.8",
+		"photon-mustache": "~>0.1.1"
 	},
 	},
+	"dflags": ["-J."],
 	"description": "Benchmark of photon-http",
 	"description": "Benchmark of photon-http",
 	"license": "BSL-1.0",
 	"license": "BSL-1.0",
 	"name": "photon-http-benchmark"
 	"name": "photon-http-benchmark"

+ 61 - 17
frameworks/D/photon-http/source/app.d

@@ -4,6 +4,8 @@ import std.array;
 import std.algorithm;
 import std.algorithm;
 import std.conv;
 import std.conv;
 import std.ascii;
 import std.ascii;
+import core.stdc.stdlib;
+import core.stdc.string;
 
 
 import mir.ser;
 import mir.ser;
 import mir.ser.json;
 import mir.ser.json;
@@ -14,13 +16,14 @@ import std.range.primitives;
 
 
 import glow.xbuf;
 import glow.xbuf;
 
 
-import photon, photon.http;
+import photon, photon.http, photon.mustache;
 
 
 import mir.random : unpredictableSeedOf;
 import mir.random : unpredictableSeedOf;
 import mir.random.variable : UniformVariable;
 import mir.random.variable : UniformVariable;
 import mir.random.engine.xorshift : Xorshift;
 import mir.random.engine.xorshift : Xorshift;
 
 
 import dpq2;
 import dpq2;
+import dpq2.conv.to_d_types;
 
 
 struct Message {
 struct Message {
     string message;
     string message;
@@ -31,6 +34,11 @@ struct WorldResponse {
 	int randomNumber;
 	int randomNumber;
 }
 }
 
 
+struct FortuneResponse {
+	int id;
+	string message;
+}
+
 enum connectionInfo = "host=tfb-database port=5432 "
 enum connectionInfo = "host=tfb-database port=5432 "
 						~ "dbname=hello_world  user=benchmarkdbuser password=benchmarkdbpass";
 						~ "dbname=hello_world  user=benchmarkdbuser password=benchmarkdbpass";
 enum worldSize = 10000;
 enum worldSize = 10000;
@@ -41,7 +49,8 @@ shared Pool!Connection connectionPool;
 class BenchmarkProcessor : HttpProcessor {
 class BenchmarkProcessor : HttpProcessor {
     HttpHeader[] plainTextHeaders = [HttpHeader("Content-Type", "text/plain; charset=utf-8")];
     HttpHeader[] plainTextHeaders = [HttpHeader("Content-Type", "text/plain; charset=utf-8")];
     HttpHeader[] jsonHeaders = [HttpHeader("Content-Type", "application/json")];
     HttpHeader[] jsonHeaders = [HttpHeader("Content-Type", "application/json")];
-    Buffer!char jsonBuf;
+    HttpHeader[] htmlHeaders = [HttpHeader("Content-Type", "text/html; charset=utf-8")];
+    Buffer!char outBuf;
     Buffer!WorldResponse worlds;
     Buffer!WorldResponse worlds;
     UniformVariable!uint uniformVariable;
     UniformVariable!uint uniformVariable;
     Xorshift gen;
     Xorshift gen;
@@ -50,7 +59,7 @@ class BenchmarkProcessor : HttpProcessor {
 		super(sock);
 		super(sock);
         gen = Xorshift(unpredictableSeed!uint);
         gen = Xorshift(unpredictableSeed!uint);
 		uniformVariable = UniformVariable!uint(1, worldSize);
 		uniformVariable = UniformVariable!uint(1, worldSize);
-		jsonBuf = Buffer!char(256);
+		outBuf = Buffer!char(256);
         worlds = Buffer!WorldResponse(500);
         worlds = Buffer!WorldResponse(500);
 	}
 	}
 
 
@@ -65,8 +74,9 @@ class BenchmarkProcessor : HttpProcessor {
             queries(req.uri);
             queries(req.uri);
         } else if(req.uri.startsWith("/updates")) {
         } else if(req.uri.startsWith("/updates")) {
             updates(req.uri);
             updates(req.uri);
-        }
-        else {
+        } else if(req.uri == "/fortunes") {
+            fortunes();
+        } else {
 			respondWith("Not found", HttpStatus.NotFound, plainTextHeaders);
 			respondWith("Not found", HttpStatus.NotFound, plainTextHeaders);
 		}
 		}
     }
     }
@@ -76,13 +86,13 @@ class BenchmarkProcessor : HttpProcessor {
     }
     }
 
 
     final void json() {
     final void json() {
-        jsonBuf.clear();
-        serializeJsonPretty!""(jsonBuf, Message("Hello, World!"));
-        respondWith(jsonBuf.data, HttpStatus.OK, jsonHeaders);
+        outBuf.clear();
+        serializeJsonPretty!""(outBuf, Message("Hello, World!"));
+        respondWith(outBuf.data, HttpStatus.OK, jsonHeaders);
     }
     }
 
 
     final void db() {
     final void db() {
-        jsonBuf.clear();
+        outBuf.clear();
         int id = uniformVariable(gen);
         int id = uniformVariable(gen);
         auto c = connectionPool.acquire();
         auto c = connectionPool.acquire();
         scope(exit) connectionPool.release(c);
         scope(exit) connectionPool.release(c);
@@ -91,13 +101,13 @@ class BenchmarkProcessor : HttpProcessor {
 		qp.argsVariadic(id);
 		qp.argsVariadic(id);
 		immutable result = c.execPrepared(qp).rangify.front;
 		immutable result = c.execPrepared(qp).rangify.front;
 		auto w = WorldResponse(id, result[0].as!PGinteger);
 		auto w = WorldResponse(id, result[0].as!PGinteger);
-        serializeJsonPretty!""(jsonBuf, w);
-        respondWith(jsonBuf.data, HttpStatus.OK, jsonHeaders);
+        serializeJsonPretty!""(outBuf, w);
+        respondWith(outBuf.data, HttpStatus.OK, jsonHeaders);
     }
     }
 
 
     // GET /queries?queries=...
     // GET /queries?queries=...
     final void queries(const(char)[] uri) {
     final void queries(const(char)[] uri) {
-        jsonBuf.clear();
+        outBuf.clear();
         worlds.clear();
         worlds.clear();
         auto c = connectionPool.acquire();
         auto c = connectionPool.acquire();
         scope(exit) connectionPool.release(c);
         scope(exit) connectionPool.release(c);
@@ -118,13 +128,13 @@ class BenchmarkProcessor : HttpProcessor {
 			immutable result = c.execPrepared(qp).rangify.front;
 			immutable result = c.execPrepared(qp).rangify.front;
 			worlds.put(WorldResponse(id, result[0].as!PGinteger));
 			worlds.put(WorldResponse(id, result[0].as!PGinteger));
 		}
 		}
-        serializeJsonPretty!""(jsonBuf, worlds.data);
-        respondWith(jsonBuf.data, HttpStatus.OK, jsonHeaders);
+        serializeJsonPretty!""(outBuf, worlds.data);
+        respondWith(outBuf.data, HttpStatus.OK, jsonHeaders);
     }
     }
     
     
     // GET /updates?queries=...
     // GET /updates?queries=...
     final void updates(const(char)[] uri) {
     final void updates(const(char)[] uri) {
-        jsonBuf.clear();
+        outBuf.clear();
         worlds.clear();
         worlds.clear();
         auto c = connectionPool.acquire();
         auto c = connectionPool.acquire();
         scope(exit) connectionPool.release(c);
         scope(exit) connectionPool.release(c);
@@ -157,11 +167,44 @@ class BenchmarkProcessor : HttpProcessor {
 			c.execPrepared(qp_update);
 			c.execPrepared(qp_update);
             worlds.put(w);
             worlds.put(w);
 		}
 		}
-        serializeJsonPretty!""(jsonBuf, worlds.data);
-        respondWith(jsonBuf.data, HttpStatus.OK, jsonHeaders);
+        serializeJsonPretty!""(outBuf, worlds.data);
+        respondWith(outBuf.data, HttpStatus.OK, jsonHeaders);
+    }
+
+    final void fortunes() {
+        outBuf.clear();
+        auto c = connectionPool.acquire();
+        scope(exit) connectionPool.release(c);
+        import std.algorithm : map, sort;
+
+		auto buf = Buffer!FortuneResponse(20);
+		QueryParams qp;
+		qp.preparedStatementName("fortune_prpq");
+		auto result = c.execPrepared(qp).rangify;
+		foreach (ref f; result) {
+            buf.put(FortuneResponse(f[0].as!PGinteger, f[1].data.alloced));
+        }
+		buf.put(FortuneResponse(0, "Additional fortune added at request time."));
+        auto data = buf.data;
+		data.sort!((a, b) => a.message < b.message);
+		mustache!(import("template.mustache"))(data, outBuf);
+        foreach (ref v; data) {
+            if (v.id != 0) dealloc(v.message);
+        }
+        respondWith(outBuf.data, HttpStatus.OK, htmlHeaders);
     }
     }
 }
 }
 
 
+string alloced(const(ubyte)[] data) {
+    void* ptr = malloc(data.length);
+    memcpy(ptr, data.ptr, data.length);
+    return (cast(immutable(char)*)ptr)[0..data.length];
+}
+
+void dealloc(const(char)[] slice) {
+    free(cast(void*)slice.ptr);
+}
+
 void server_worker(Socket client) {
 void server_worker(Socket client) {
     scope processor =  new BenchmarkProcessor(client);
     scope processor =  new BenchmarkProcessor(client);
     try {
     try {
@@ -201,6 +244,7 @@ void main() {
     initPhoton();
     initPhoton();
     connectionPool = pool(poolSize, 15.seconds, () {
     connectionPool = pool(poolSize, 15.seconds, () {
         auto c = new Connection(connectionInfo);
         auto c = new Connection(connectionInfo);
+        c.prepareEx("fortune_prpq", "SELECT id, message::text FROM Fortune");
         c.prepareEx("db_prpq", "SELECT randomNumber, id FROM world WHERE id = $1");
         c.prepareEx("db_prpq", "SELECT randomNumber, id FROM world WHERE id = $1");
         c.prepareEx("db_update_prpq", "UPDATE world SET randomNumber = $1  WHERE id = $2");
         c.prepareEx("db_update_prpq", "UPDATE world SET randomNumber = $1  WHERE id = $2");
         return c;
         return c;

+ 12 - 0
frameworks/D/photon-http/template.mustache

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+ <html>
+ <head><title>Fortunes</title></head>
+ <body>
+ <table>
+ <tr><th>id</th><th>message</th></tr>
+ {{#.}}
+ <tr><td>{{id}}</td><td>{{message}}</td></tr>
+ {{/.}}
+ </table>
+ </body>
+ </html>