ソースを参照

added deno oak framework (#7385)

Signed-off-by: Shagit Ziganshin <[email protected]>
Shagit Ziganshin 3 年 前
コミット
a0a7cabdf7

+ 48 - 0
frameworks/TypeScript/oak/README.md

@@ -0,0 +1,48 @@
+# oak Benchmarking Test
+
+### Test Type Implementation Source Code
+
+- [PLAINTEXT](src/routes.ts#L15)
+- [JSON](src/routes.ts#L16)
+- [DB](src/routes.ts#L17)
+- [QUERY](src/routes.ts#L24)
+- [UPDATE](src/routes.ts#L37)
+- [FORTUNES](src/routes.ts#L53)
+- [CACHED QUERY](src/routes.ts#L67)
+
+## Important Libraries
+
+The tests were run with:
+
+- [deno](https://deno.land)
+- [oak](https://deno.land/x/oak/)
+
+## Test URLs
+
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/query?q=
+
+### UPDATE
+
+http://localhost:8080/update?q=
+
+### FORTUNES
+
+http://localhost:8080/fortunes
+
+### CACHED QUERY
+
+http://localhost:8080/cached_query?q=

+ 31 - 0
frameworks/TypeScript/oak/benchmark_config.json

@@ -0,0 +1,31 @@
+{
+  "framework": "oak",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?q=",
+        "update_url": "/updates?q=",
+        "fortune_url": "/fortunes",
+        "cached_query_url": "/cached_queries?q=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "postgres",
+        "framework": "None",
+        "language": "Typescript",
+        "flavor": "deno",
+        "orm": "Micro",
+        "platform": "deno",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "oak",
+        "notes": "",
+        "versus": "nodejs"
+      }
+    }
+  ]
+}

+ 13 - 0
frameworks/TypeScript/oak/oak.dockerfile

@@ -0,0 +1,13 @@
+FROM denoland/deno
+
+EXPOSE 8080
+
+WORKDIR /app
+
+USER deno
+
+COPY ./src/deps.ts .
+RUN deno cache deps.ts
+
+ADD ./src .
+CMD [ "run", "--allow-net", "main.ts" ]

+ 20 - 0
frameworks/TypeScript/oak/src/deps.ts

@@ -0,0 +1,20 @@
+export {
+  Application,
+  Context,
+  Router,
+  Status,
+} from "https://deno.land/x/[email protected]/mod.ts";
+export { getQuery } from "https://deno.land/x/[email protected]/helpers.ts";
+export type { ResponseBody } from "https://deno.land/x/[email protected]/response.ts";
+
+export {
+  Column,
+  connect,
+  DataType,
+  Manager,
+  Model,
+  Primary,
+} from "https://deno.land/x/[email protected]/mod.ts";
+export type { DatabaseResult } from "https://deno.land/x/[email protected]/src/adapters/adapter.ts";
+
+export { html } from "https://deno.land/x/[email protected]/mod.ts";

+ 13 - 0
frameworks/TypeScript/oak/src/helpers.ts

@@ -0,0 +1,13 @@
+import { Context, ResponseBody, Status } from "./deps.ts";
+
+export function Ok(ctx: Context, body: ResponseBody) {
+  ctx.response.status = Status.OK;
+  ctx.response.body = body;
+  return;
+}
+
+export function NotFound(ctx: Context) {
+  ctx.response.status = Status.NotFound;
+  ctx.response.body = "Not found";
+  return;
+}

+ 27 - 0
frameworks/TypeScript/oak/src/main.ts

@@ -0,0 +1,27 @@
+import { Application, DatabaseResult, Manager } from "./deps.ts";
+import { router } from "./routes.ts";
+import { getDbClient } from "./utils.ts";
+
+const app = new Application<
+  { manager: Manager; cached_worlds: DatabaseResult[] }
+>();
+
+// headers
+app.use(async (ctx, next) => {
+  ctx.response.headers.set("Date", new Date().toUTCString());
+  ctx.response.headers.set("Server", "Oak");
+  await next();
+});
+
+// database handling
+app.use(async (ctx, next) => {
+  const db = await getDbClient();
+  ctx.state.manager = db.getManager();
+  await next();
+  await db.disconnect();
+});
+
+app.use(router.routes());
+app.use(router.allowedMethods());
+
+await app.listen({ port: 8080 });

+ 19 - 0
frameworks/TypeScript/oak/src/models.ts

@@ -0,0 +1,19 @@
+import { Column, DataType, Model, Primary } from "./deps.ts";
+
+@Model()
+export class World {
+  @Primary()
+  id!: number;
+
+  @Column({ type: DataType.Number })
+  randomnumber!: number;
+}
+
+@Model()
+export class Fortune {
+  @Primary()
+  id!: number;
+
+  @Column({ type: DataType.String })
+  message!: string;
+}

+ 76 - 0
frameworks/TypeScript/oak/src/routes.ts

@@ -0,0 +1,76 @@
+import { Fortune, World } from "./models.ts";
+import { NotFound, Ok } from "./helpers.ts";
+import {
+  getDbClient,
+  parseQuery,
+  randomNumber,
+  renderTemplate,
+} from "./utils.ts";
+import { Router } from "./deps.ts";
+
+const cached_worlds = await (await getDbClient()).getManager().query(World)
+  .limit(10000).all();
+
+export const router = new Router()
+  .get("/plaintext", (ctx) => Ok(ctx, "Hello, World!"))
+  .get("/json", (ctx) => Ok(ctx, { message: "Hello, World!" }))
+  .get("/db", async (ctx) => {
+    const world = await ctx.state.manager.query(World).where(
+      "id",
+      randomNumber(),
+    ).first();
+    Ok(ctx, world);
+  })
+  .get("/queries", async (ctx) => {
+    const worlds = [];
+    const queries = parseQuery(ctx);
+    for (let i = 0; i < queries; i++) {
+      const world = await ctx.state.manager.query(World).where(
+        "id",
+        randomNumber(),
+      ).first();
+      worlds.push(world);
+    }
+
+    Ok(ctx, worlds);
+  })
+  .get("/updates", async (ctx) => {
+    const worlds = [];
+    const queries = parseQuery(ctx);
+
+    for (let i = 0; i < queries; i++) {
+      const world = await ctx.state.manager.query(World).where(
+        "id",
+        randomNumber(),
+      ).first();
+      world.randomnumber = randomNumber();
+      worlds.push(world);
+
+      await ctx.state.manager.save(world);
+    }
+    Ok(ctx, worlds);
+  })
+  .get("/fortunes", async (ctx) => {
+    const fortunes: Fortune[] = await ctx.state.manager.query(Fortune).all();
+    fortunes.push({
+      id: 0,
+      message: "Additional fortune added at request time.",
+    });
+
+    fortunes.sort((a: Fortune, b: Fortune) =>
+      a.message.localeCompare(b.message)
+    );
+
+    ctx.response.headers.set("Content-Type", "text/html; charset=utf-8");
+    Ok(ctx, renderTemplate(fortunes));
+  })
+  .get("/cached_queries", (ctx) => {
+    const queries = parseQuery(ctx);
+    const worlds = [];
+
+    for (let i = 0; i < queries; i++) {
+      worlds.push(cached_worlds[randomNumber()]);
+    }
+    Ok(ctx, worlds);
+  })
+  .get("/(.*)", (ctx) => NotFound(ctx));

+ 41 - 0
frameworks/TypeScript/oak/src/utils.ts

@@ -0,0 +1,41 @@
+import { connect, Context, getQuery, html } from "./deps.ts";
+import { Fortune } from "./models.ts";
+
+export const randomNumber = () => {
+  return Math.floor(Math.random() * 10000 + 1);
+};
+
+export const parseQuery = (ctx: Context) => {
+  return Math.min(parseInt(getQuery(ctx).q) || 1, 500);
+};
+
+export const renderTemplate = (fortunes: Fortune[]) => {
+  return `<!doctype html>
+  <html>
+    <head><title>Fortunes</title></head>
+    <body>
+      <table>
+        <tr>
+          <th>id</th>
+          <th>message</th>
+        </tr>
+        ${
+    fortunes.map(({ id, message }) =>
+      html`<tr><td>${id}</td><td>${message}</td></tr>`
+    ).join("")
+  }
+      </table>
+    </body>
+  </html>`;
+};
+
+export async function getDbClient() {
+  return await connect({
+    type: "postgres",
+    port: 5432,
+    database: "hello_world",
+    hostname: "tfb-database",
+    username: "benchmarkdbuser",
+    password: "benchmarkdbpass",
+  });
+}

+ 6 - 0
frameworks/TypeScript/oak/tsconfig.json

@@ -0,0 +1,6 @@
+{
+  "compilerOptions": {
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true
+  }
+}