The framework's own conditional-request machinery: the validator it computes over the body, and the 304 it answers.
Comparable across every framework at one body size. Not across sizes: the small row hashes a row and the large row hashes fourteen hundred, and the ratio between them is a hash rate.
| test | heft | request | base | what differs from the base |
|---|---|---|---|---|
| etag.large | 4 | GET /etag/large | json.large | the framework hashing the body and writing the ETag it computed onto the response |
| etag.match_large | 4 | GET /etag/large | etag.large | a conditional request answered 304, with no body |
| etag.stale_large | 4 | GET /etag/large | etag.large | a conditional request whose validator does not match, answered in full |
| etag.small | 1 | GET /etag/small | json.small | the framework hashing the body and writing the ETag it computed onto the response |
The framework hashing the body it is about to send and writing the validator it computed onto the response. Read against json.large, the difference is the hash and the header. The tag is the framework's own, so no two of them agree on it.
GET /etag/large
No body.
HTTP 200
payload items.large
{"size":"large","count":1425,"items":[{"id":1,"name":"slate-lamp-6647","category":"tools","priceCents":18928,"inStock":true},{"id":2,"name":"brass-hinge-5716","category":"garden","priceCents":17577,"inStock":true},{"id":3,"name":"linen-ring-6971","category":"kitchen","priceCents":5540,"inStock":true},{"id":4,"name":"copper-bolt-9217","category":"outdoor","priceCents":11562,"inStock":true},{"id":5,"name":"linen-lamp-8288","category":"office","priceCents":1413,"inStock":true},{"id":6,"name":"copper-pan-9308","category":"tools","priceCents":10795,"inStock":true},{"id":7,"name":"slate-seed-3051","category":"garden","priceCents":9667,"inStock":false},{"id":8,"name":"oak-lamp-8662","category":"kit
… 128,510 bytes totalCompared as parsed JSON, so key order and how a number is written do not matter.
import { performanceTest } from "#kit";
import { items } from "#payloads";
const path = "/etag/large";
export default performanceTest({
id: { family: "etag", name: "large" },
path,
base: "json.large",
varies: "validators",
heft: 4,
about:
"The framework hashing the body it is about to send and writing the " +
"validator it computed onto the response. Read against json.large, the " +
"difference is the hash and the header. The tag is the framework's own, " +
"so no two of them agree on it.",
request: (c) => c.get(path).okWith(items.large).hasHeader("etag").fresh(),
});
The only row that cannot be sent until the framework has answered a different one: the validator is the framework's to produce. A 304 saves the write and nothing else, because the body is built and hashed before anything is compared.
Before this request, the test sends GET /etag/large once and reads its etag. That request is not measured.
GET /etag/large
No body.
HTTP 304
No body.
import { performanceTest } from "#kit";
const path = "/etag/large";
export default performanceTest({
id: { family: "etag", name: "match_large" },
path,
base: "etag.large",
varies: "conditional",
heft: 4,
about:
"The only row that cannot be sent until the framework has answered a " +
"different one: the validator is the framework's to produce. A 304 saves " +
"the write and nothing else, because the body is built and hashed before " +
"anything is compared.",
request: async (c) => {
const tag = await c.once(path, () => c.get(path).etag());
return c.get(path).header("if-none-match", tag).notModified().emptyBody();
},
});
A conditional request whose validator does not match, answered in full. Read against etag.match_large, the difference is the write the 304 saved, and read against etag.large it is the comparison that failed.
GET /etag/large
No body.
HTTP 200
payload items.large
{"size":"large","count":1425,"items":[{"id":1,"name":"slate-lamp-6647","category":"tools","priceCents":18928,"inStock":true},{"id":2,"name":"brass-hinge-5716","category":"garden","priceCents":17577,"inStock":true},{"id":3,"name":"linen-ring-6971","category":"kitchen","priceCents":5540,"inStock":true},{"id":4,"name":"copper-bolt-9217","category":"outdoor","priceCents":11562,"inStock":true},{"id":5,"name":"linen-lamp-8288","category":"office","priceCents":1413,"inStock":true},{"id":6,"name":"copper-pan-9308","category":"tools","priceCents":10795,"inStock":true},{"id":7,"name":"slate-seed-3051","category":"garden","priceCents":9667,"inStock":false},{"id":8,"name":"oak-lamp-8662","category":"kit
… 128,510 bytes totalCompared as parsed JSON, so key order and how a number is written do not matter.
import { performanceTest } from "#kit";
import { items } from "#payloads";
const path = "/etag/large";
/** A validator no framework computes. */
const stale = '"0000000000000000"';
export default performanceTest({
id: { family: "etag", name: "stale_large" },
path,
base: "etag.large",
varies: "stale_validator",
heft: 4,
about:
"A conditional request whose validator does not match, answered in full. " +
"Read against etag.match_large, the difference is the write the 304 " +
"saved, and read against etag.large it is the comparison that failed.",
request: (c) => c.get(path).header("if-none-match", stale).okWith(items.large).fresh(),
});
The framework hashing the body it is about to send and writing the validator it computed onto the response. Read against json.small, the difference is the hash and the header. The tag is the framework's own, so no two of them agree on it.
GET /etag/small
No body.
HTTP 200
payload items.small
{"size":"small","count":1,"items":[{"id":1,"name":"slate-lamp-6647","category":"tools","priceCents":18928,"inStock":true}]}Compared as parsed JSON, so key order and how a number is written do not matter.
import { performanceTest } from "#kit";
import { items } from "#payloads";
const path = "/etag/small";
export default performanceTest({
id: { family: "etag", name: "small" },
path,
base: "json.small",
varies: "validators",
heft: 1,
about:
"The framework hashing the body it is about to send and writing the " +
"validator it computed onto the response. Read against json.small, the " +
"difference is the hash and the header. The tag is the framework's own, " +
"so no two of them agree on it.",
request: (c) => c.get(path).okWith(items.small).hasHeader("etag").fresh(),
});