diff --git a/frameworks/elysia/server.ts b/frameworks/elysia/server.ts index 61c4bedb1..8c074e4d3 100644 --- a/frameworks/elysia/server.ts +++ b/frameworks/elysia/server.ts @@ -1,5 +1,6 @@ import { Elysia, status } from "elysia"; import { staticPlugin } from "@elysiajs/static"; +import { gzipSync, brotliCompressSync } from "node:zlib"; import { SQL } from "bun"; @@ -26,14 +27,88 @@ if (cluster.isPrimary) { : undefined; pg?.connect().catch((e) => console.error("pg connect failed:", e)); + // standard mode: Elysia ships no official compression plugin, and the + // community ones (elysia-compress and its forks) import APIs removed in + // elysia >= 1.2.6 (vermaysha/elysia-compress#149; the repo is archived). + // The framework's documented compression mechanism is the mapResponse + // lifecycle hook — the block below is the official docs example + // (elysiajs.com/essential/life-cycle) extended with real Accept-Encoding + // negotiation. Compression runs per request through node:zlib at default + // settings: no caches, no pre-compressed bodies. + const encoder = new TextEncoder(); + const THRESHOLD = 1024; // same default as the express/fastify middleware + + // First acceptable encoding in the client's own order, honouring q=0. + const pickEncoding = (accept: string | undefined) => { + if (!accept) return null; + for (const part of accept.split(",")) { + const [name, ...params] = part.trim().split(";"); + const enc = name.trim().toLowerCase(); + if (enc !== "br" && enc !== "gzip") continue; + const q = params.find((p) => p.trim().startsWith("q=")); + if (q && parseFloat(q.trim().slice(2)) <= 0) continue; + return enc; + } + return null; + }; + new Elysia() .headers({ server: "Elysia", }) + // The static plugin mounts BEFORE the compression hook on purpose: + // Elysia hooks only apply to routes registered after them, and letting + // mapResponse wrap the plugin's not-found flow swallows its 404 into + // an empty 200. Static files keep the default (uncompressed) path. .use(staticPlugin({ assets: "/data/static", prefix: "/static", })) + .mapResponse(({ responseValue, set, headers }) => { + // Ready-made Responses and files pass through untouched. + if (responseValue instanceof Response || responseValue instanceof Blob) + return; + + // Elysia custom-status objects (status() returns, plugin 404s): + // once a mapResponse hook is registered, falling through here + // loses the status and yields an empty 200 — map them explicitly. + const rv = responseValue as any; + if (typeof rv?.code === "number" && set.status !== 200) { + const inner = rv.response; + return new Response( + typeof inner === "string" + ? inner + : inner != null + ? JSON.stringify(inner) + : "", + { status: rv.code }, + ); + } + + // headers is absent on the error path — the hook must pass errors + // through, not throw its own 500. + const encoding = pickEncoding(headers?.["accept-encoding"]); + if (!encoding) return; + + const isJson = typeof responseValue === "object"; + const text = isJson + ? JSON.stringify(responseValue) + : (responseValue?.toString() ?? ""); + if (text.length < THRESHOLD) return; + + set.headers["content-encoding"] = encoding; + const body = encoder.encode(text); + return new Response( + encoding === "br" ? brotliCompressSync(body) : gzipSync(body), + { + headers: { + "content-type": `${ + isJson ? "application/json" : "text/plain" + }; charset=utf-8`, + }, + }, + ); + }) .get("/pipeline", ({ set }) => { set.headers["content-type"] = "text/plain"; return "ok"; @@ -63,7 +138,7 @@ if (cluster.isPrimary) { for (const v of Object.values(query)) sum += +v || 0; return sum; }) - .get("/json/:count", ({ params, query, headers, set }) => { + .get("/json/:count", ({ params, query }) => { const count = Math.max( 0, Math.min(+params.count || 0, datasetItems.length), @@ -85,25 +160,8 @@ if (cluster.isPrimary) { })), }; - const encoding = headers["accept-encoding"]; - if (encoding) { - const index = encoding.indexOf(","); - const type = - index === -1 ? encoding : encoding.slice(0, index); - - set.headers["content-type"] = "application/json"; - if (type === "gzip") { - set.headers["content-encoding"] = "gzip"; - return Bun.gzipSync(JSON.stringify(result)); - } else if (encoding === "br") { - set.headers["content-encoding"] = "br"; - return Bun.deflateSync(JSON.stringify(result)); - } else if (encoding === "deflate") { - set.headers["content-encoding"] = "deflate"; - return Bun.deflateSync(JSON.stringify(result)); - } - } - + // json-comp negotiation belongs to the compression plugin mounted + // above; the handler only serializes. return result; }) .get("/async-db", async ({ query }) => { diff --git a/site/data/results/elysia.json b/site/data/results/elysia.json index 2c77c854f..5ad765db1 100644 --- a/site/data/results/elysia.json +++ b/site/data/results/elysia.json @@ -4,68 +4,71 @@ "api-16-1024": { "framework": "elysia", "language": "TS", - "rps": 115633, - "avg_latency": "7.24ms", - "p99_latency": "32.90ms", - "cpu": "1401.7%", - "memory": "1.2GiB", + "rps": 124590, + "avg_latency": "6.42ms", + "p99_latency": "29.70ms", + "cpu": "1488.4%", + "memory": "1.3GiB", "connections": 1024, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "584.63MB/s", - "reconnects": 346811, - "status_2xx": 1734501, + "bandwidth": "629.99MB/s", + "input_bw": "7.01MB/s", + "reconnects": 373785, + "status_2xx": 1868858, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0, - "tpl_baseline": 650325, - "tpl_json": 650846, + "tpl_baseline": 700604, + "tpl_json": 701110, "tpl_db": 0, "tpl_upload": 0, "tpl_static": 0, - "tpl_async_db": 433330 + "tpl_async_db": 467144 }, "api-4-256": { "framework": "elysia", "language": "TS", - "rps": 47070, - "avg_latency": "4.02ms", - "p99_latency": "19.60ms", - "cpu": "359.7%", - "memory": "319MiB", + "rps": 36425, + "avg_latency": "5.60ms", + "p99_latency": "30.10ms", + "cpu": "383.6%", + "memory": "296MiB", "connections": 256, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "237.93MB/s", - "reconnects": 141220, - "status_2xx": 706053, + "bandwidth": "184.03MB/s", + "input_bw": "2.05MB/s", + "reconnects": 109272, + "status_2xx": 546378, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0, - "tpl_baseline": 264809, - "tpl_json": 264686, + "tpl_baseline": 205080, + "tpl_json": 204798, "tpl_db": 0, "tpl_upload": 0, "tpl_static": 0, - "tpl_async_db": 176558 + "tpl_async_db": 136499 }, "async-db-1024": { "framework": "elysia", "language": "TS", - "rps": 234748, - "avg_latency": "3.80ms", - "p99_latency": "20.40ms", - "cpu": "5000.3%", + "rps": 246796, + "avg_latency": "3.61ms", + "p99_latency": "15.90ms", + "cpu": "5442.0%", "memory": "4.8GiB", "connections": 1024, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "902.60MB/s", - "reconnects": 93889, - "status_2xx": 2347481, + "bandwidth": "948.90MB/s", + "input_bw": "16.48MB/s", + "reconnects": 98740, + "status_2xx": 2467969, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -73,18 +76,19 @@ "baseline-4096": { "framework": "elysia", "language": "TS", - "rps": 1836710, - "avg_latency": "2.23ms", - "p99_latency": "10.00ms", - "cpu": "5806.3%", - "memory": "1.5GiB", + "rps": 1439469, + "avg_latency": "2.85ms", + "p99_latency": "8.52ms", + "cpu": "6428.0%", + "memory": "1.6GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "231.15MB/s", + "bandwidth": "182.53MB/s", + "input_bw": "111.20MB/s", "reconnects": 0, - "status_2xx": 9183552, + "status_2xx": 7197345, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -92,18 +96,19 @@ "baseline-512": { "framework": "elysia", "language": "TS", - "rps": 1815797, - "avg_latency": "281us", - "p99_latency": "2.03ms", - "cpu": "5900.5%", - "memory": "1.4GiB", + "rps": 1425861, + "avg_latency": "358us", + "p99_latency": "2.16ms", + "cpu": "6445.8%", + "memory": "1.7GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "228.52MB/s", + "bandwidth": "180.81MB/s", + "input_bw": "110.14MB/s", "reconnects": 0, - "status_2xx": 9078989, + "status_2xx": 7129307, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -111,18 +116,19 @@ "json-4096": { "framework": "elysia", "language": "TS", - "rps": 877405, - "avg_latency": "4.35ms", - "p99_latency": "16.30ms", - "cpu": "5814.2%", - "memory": "1.5GiB", + "rps": 904900, + "avg_latency": "4.20ms", + "p99_latency": "12.50ms", + "cpu": "6294.2%", + "memory": "1.7GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "2.99GB/s", - "reconnects": 173578, - "status_2xx": 4387026, + "bandwidth": "3.08GB/s", + "input_bw": "43.15MB/s", + "reconnects": 179930, + "status_2xx": 4524500, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -130,19 +136,19 @@ "json-comp-16384": { "framework": "elysia", "language": "TS", - "rps": 378203, - "avg_latency": "42.71ms", - "p99_latency": "69.30ms", - "cpu": "6437.1%", - "memory": "2.0GiB", + "rps": 329788, + "avg_latency": "49.27ms", + "p99_latency": "82.30ms", + "cpu": "6398.1%", + "memory": "2.5GiB", "connections": 16384, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "507.53MB/s", - "input_bw": "28.13MB/s", - "reconnects": 66976, - "status_2xx": 1891018, + "bandwidth": "447.21MB/s", + "input_bw": "24.53MB/s", + "reconnects": 58485, + "status_2xx": 1648944, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -150,19 +156,19 @@ "json-comp-4096": { "framework": "elysia", "language": "TS", - "rps": 372779, - "avg_latency": "10.95ms", - "p99_latency": "24.80ms", - "cpu": "6054.4%", - "memory": "1.6GiB", + "rps": 321562, + "avg_latency": "12.70ms", + "p99_latency": "29.70ms", + "cpu": "6150.6%", + "memory": "2.0GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "500.20MB/s", - "input_bw": "27.73MB/s", - "reconnects": 72567, - "status_2xx": 1863895, + "bandwidth": "436.00MB/s", + "input_bw": "23.92MB/s", + "reconnects": 62609, + "status_2xx": 1607811, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -170,19 +176,19 @@ "json-comp-512": { "framework": "elysia", "language": "TS", - "rps": 348593, - "avg_latency": "1.46ms", - "p99_latency": "6.99ms", - "cpu": "5940.0%", - "memory": "1.7GiB", + "rps": 296545, + "avg_latency": "1.72ms", + "p99_latency": "8.05ms", + "cpu": "5933.4%", + "memory": "1.8GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "467.72MB/s", - "input_bw": "25.93MB/s", - "reconnects": 69738, - "status_2xx": 1742965, + "bandwidth": "402.10MB/s", + "input_bw": "22.06MB/s", + "reconnects": 59300, + "status_2xx": 1482726, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -190,18 +196,19 @@ "limited-conn-4096": { "framework": "elysia", "language": "TS", - "rps": 1566288, - "avg_latency": "2.60ms", - "p99_latency": "13.50ms", - "cpu": "5670.5%", - "memory": "1.5GiB", + "rps": 1273447, + "avg_latency": "3.20ms", + "p99_latency": "11.80ms", + "cpu": "6286.8%", + "memory": "1.6GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "197.10MB/s", - "reconnects": 781752, - "status_2xx": 7831442, + "bandwidth": "161.48MB/s", + "input_bw": "98.37MB/s", + "reconnects": 636345, + "status_2xx": 6367239, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -209,18 +216,19 @@ "limited-conn-512": { "framework": "elysia", "language": "TS", - "rps": 1279234, - "avg_latency": "391us", - "p99_latency": "3.76ms", - "cpu": "5212.1%", - "memory": "1.1GiB", + "rps": 1149028, + "avg_latency": "436us", + "p99_latency": "2.61ms", + "cpu": "6100.4%", + "memory": "1.6GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "161.02MB/s", - "reconnects": 639642, - "status_2xx": 6396171, + "bandwidth": "145.70MB/s", + "input_bw": "88.76MB/s", + "reconnects": 574542, + "status_2xx": 5745142, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -228,18 +236,18 @@ "pipelined-4096": { "framework": "elysia", "language": "TS", - "rps": 4990677, - "avg_latency": "13.12ms", - "p99_latency": "16.50ms", - "cpu": "6373.5%", - "memory": "1.0GiB", + "rps": 325991, + "avg_latency": "157.72ms", + "p99_latency": "235.70ms", + "cpu": "6717.9%", + "memory": "1.6GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 16, - "bandwidth": "566.22MB/s", - "reconnects": 0, - "status_2xx": 24953389, + "bandwidth": "37.59MB/s", + "reconnects": 44, + "status_2xx": 1629959, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -247,18 +255,18 @@ "pipelined-512": { "framework": "elysia", "language": "TS", - "rps": 4654576, - "avg_latency": "1.76ms", - "p99_latency": "2.93ms", - "cpu": "6438.9%", - "memory": "1016MiB", + "rps": 91526, + "avg_latency": "15.44ms", + "p99_latency": "41.90ms", + "cpu": "2153.3%", + "memory": "1.4GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 16, - "bandwidth": "528.14MB/s", - "reconnects": 0, - "status_2xx": 23272884, + "bandwidth": "10.57MB/s", + "reconnects": 8, + "status_2xx": 457630, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -266,17 +274,18 @@ "static-1024": { "framework": "elysia", "language": "TS", - "rps": 554634, - "avg_latency": "2.18ms", - "cpu": "5806.4%", - "memory": "2.2GiB", + "rps": 86268, + "avg_latency": "12.31ms", + "p99_latency": "65.81ms", + "cpu": "6321.3%", + "memory": "3.3GiB", "connections": 1024, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "32.94GB", + "bandwidth": "5.13GB", "reconnects": 0, - "status_2xx": 2828532, + "status_2xx": 439975, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -284,17 +293,18 @@ "static-4096": { "framework": "elysia", "language": "TS", - "rps": 567171, - "avg_latency": "7.27ms", - "cpu": "5885.7%", - "memory": "2.2GiB", + "rps": 85903, + "avg_latency": "47.35ms", + "p99_latency": "131.69ms", + "cpu": "6365.6%", + "memory": "3.4GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "33.69GB", + "bandwidth": "5.10GB", "reconnects": 0, - "status_2xx": 2892540, + "status_2xx": 438124, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -302,17 +312,18 @@ "static-6800": { "framework": "elysia", "language": "TS", - "rps": 574100, - "avg_latency": "11.78ms", - "cpu": "5874.9%", - "memory": "1.3GiB", + "rps": 86966, + "avg_latency": "77.25ms", + "p99_latency": "220.70ms", + "cpu": "6329.4%", + "memory": "3.5GiB", "connections": 6800, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "34.10GB", + "bandwidth": "5.17GB", "reconnects": 0, - "status_2xx": 2928604, + "status_2xx": 443515, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -320,18 +331,19 @@ "upload-256": { "framework": "elysia", "language": "TS", - "rps": 1570, - "avg_latency": "156.00ms", - "p99_latency": "1.21s", - "cpu": "8177.7%", - "memory": "3.0GiB", + "rps": 1668, + "avg_latency": "147.04ms", + "p99_latency": "1.10s", + "cpu": "6134.6%", + "memory": "3.3GiB", "connections": 256, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "190.72KB/s", - "reconnects": 1544, - "status_2xx": 7866, + "bandwidth": "202.38KB/s", + "input_bw": "13.23GB/s", + "reconnects": 1634, + "status_2xx": 8341, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -339,18 +351,19 @@ "upload-32": { "framework": "elysia", "language": "TS", - "rps": 1502, - "avg_latency": "21.25ms", - "p99_latency": "128.50ms", - "cpu": "3839.6%", - "memory": "2.6GiB", + "rps": 1462, + "avg_latency": "21.83ms", + "p99_latency": "129.70ms", + "cpu": "4028.3%", + "memory": "2.7GiB", "connections": 32, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "182.19KB/s", - "reconnects": 1501, - "status_2xx": 7511, + "bandwidth": "177.36KB/s", + "input_bw": "11.60GB/s", + "reconnects": 1460, + "status_2xx": 7312, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0