Skip to content

Commit 2590f1d

Browse files
Cache api integration (v2) (#856)
* Add support to deno deploy cache api implementation Documentation for deno's Edge Cache are available at https://docs.deno.com/deploy/manual/edge-cache/ This commit contains the following alterations: * Add CACHE_API as default cache engine, which replaces KV in our DD websites (This does not afect the k8s websites, as they are by default using the FILE_SYSTEM cache). * Change LOADER_CACHE_START_THRESHOLD env var to 0 * Change LOADER_CACHE_SIZE to 1_024_000 * Add Content-Type field to cached response header * Add Content-Length field to cached response header if length is available * Great cache revamp * Refact cache * Enable loader cache by default * Lint, code readability and better env names * Fixed env names * Improve env names and default * Fix format --------- Co-authored-by: Itamar Rocha Filho <[email protected]>
1 parent 5c682c4 commit 2590f1d

File tree

12 files changed

+476
-1084
lines changed

12 files changed

+476
-1084
lines changed

README.md

+5-9
Original file line numberDiff line numberDiff line change
@@ -171,17 +171,13 @@ Here is a table with the integrations that we have built and the statuses of the
171171
## Cache env vars (WIP)
172172
| Environment Variable | Description | Example Value |
173173
|-----------------------------------|---------------------------------------------------------|--------------------------------------------------------|
174-
| `CACHE_UPLOAD_BUCKET` | The AWS S3 bucket name for cache uploads | `BUCKET-NAME` |
175-
| `CACHE_AWS_REGION` | AWS region where the cache bucket is located | `sa-east-1` |
176-
| `CACHE_AWS_ACCESS_KEY_ID` | AWS access key ID for authentication | `` |
177-
| `CACHE_AWS_SECRET_ACCESS_KEY` | AWS secret access key for authentication | `` |
178174
| `ENABLE_LOADER_CACHE` | Flag to enable or disable the loader cache | `true` |
179175
| `LOADER_CACHE_START_TRESHOLD` | Cache start threshold | `0` |
180-
| `WEB_CACHE_ENGINE` | Defines the cache engine(s) to use | `"FILE_SYSTEM,S3"` |
181-
| `FILE_SYSTEM_CACHE_DIRECTORY` | Directory path for file system cache | `` |
182-
| `MAX_CACHE_SIZE` | Maximum size of the file system cache (in bytes) | `1073741824` (1 GB) |
183-
| `TTL_AUTOPURGE` | Flag to automatically delete expired items from the file system cache (cpu intensive) | `false` |
184-
| `TTL_RESOLUTION` | Time interval to check for expired items in the file system cache (in milliseconds) | `30000` (30 seconds) |
176+
| `WEB_CACHE_ENGINE` | Defines the cache engine(s) to use | `"FILE_SYSTEM,CACHE_API"` |
177+
| `FILE_SYSTEM_CACHE_DIRECTORY` | Directory path for file system cache | `/tmp` |
178+
| `CACHE_MAX_SIZE` | Maximum size of the file system cache (in bytes) | `1073741824` (1 GB) |
179+
| `CACHE_TTL_AUTOPURGE` | Flag to automatically delete expired items from the file system cache (cpu intensive) | `false` |
180+
| `CACHE_TTL_RESOLUTION` | Time interval to check for expired items in the file system cache (in milliseconds) | `30000` (30 seconds) |
185181
| `CACHE_MAX_AGE_S` | Time for cache to become stale | `60` (60 seconds) |
186182

187183

blocks/loader.ts

+18-7
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ export const wrapCaughtErrors = async <
118118
};
119119

120120
export const LOADER_CACHE_START_TRESHOLD =
121-
Deno.env.get("LOADER_CACHE_START_TRESHOLD") ?? 5;
121+
Deno.env.get("LOADER_CACHE_START_TRESHOLD") ?? 0;
122122

123-
export const LOADER_CACHE_SIZE = Deno.env.get("LOADER_CACHE_SIZE") ?? 1_024;
123+
export const LOADER_CACHE_SIZE = Deno.env.get("LOADER_CACHE_SIZE") ?? 1_024_000;
124124

125125
const stats = {
126126
cache: meter.createCounter("loader_cache", {
@@ -291,13 +291,24 @@ const wrapLoader = (
291291

292292
const callHandlerAndCache = async () => {
293293
const json = await handler(props, req, ctx);
294+
const jsonStringEncoded = new TextEncoder().encode(
295+
JSON.stringify(json),
296+
);
297+
298+
const headers: { [key: string]: string } = {
299+
expires: new Date(Date.now() + (MAX_AGE_S * 1e3))
300+
.toUTCString(),
301+
"Content-Type": "application/json",
302+
};
303+
304+
if (jsonStringEncoded && jsonStringEncoded.length > 0) {
305+
headers["Content-Length"] = "" + jsonStringEncoded.length;
306+
}
307+
294308
cache.put(
295309
request,
296-
new Response(JSON.stringify(json), {
297-
headers: {
298-
"expires": new Date(Date.now() + (MAX_AGE_S * 1e3))
299-
.toUTCString(),
300-
},
310+
new Response(jsonStringEncoded, {
311+
headers: headers,
301312
}),
302313
).catch((error) => logger.error(`loader error ${error}`));
303314

runtime/caches/common.ts

+2-41
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,6 @@
11
import { ValueType } from "../../deps.ts";
22
import { tracer } from "../../observability/otel/config.ts";
33
import { meter } from "../../observability/otel/metrics.ts";
4-
import { sha1 } from "../utils.ts";
5-
6-
export const assertNoOptions = (
7-
{ ignoreMethod, ignoreSearch, ignoreVary }: CacheQueryOptions = {},
8-
) => {
9-
if (ignoreMethod || ignoreSearch || ignoreVary) {
10-
throw new Error("Not Implemented");
11-
}
12-
};
13-
14-
export const requestURL = (request: RequestInfo | URL): string => {
15-
return typeof request === "string"
16-
? request
17-
: request instanceof URL
18-
? request.href
19-
: request.url;
20-
};
21-
22-
export const withCacheNamespace =
23-
(cacheName: string) => (request: RequestInfo | URL): Promise<string> => {
24-
return requestURLSHA1(request).then((key) => `${key}${cacheName}`);
25-
};
26-
27-
export const requestURLSHA1 = (request: RequestInfo | URL): Promise<string> => {
28-
return sha1(requestURL(request));
29-
};
30-
31-
export const assertCanBeCached = (req: Request, response: Response) => {
32-
if (!/^http(s?):\/\//.test(req.url)) {
33-
throw new TypeError(
34-
"Request url protocol must be 'http:' or 'https:'",
35-
);
36-
}
37-
if (req.method !== "GET") {
38-
throw new TypeError("Request method must be GET");
39-
}
40-
41-
if (response.status === 206) {
42-
throw new TypeError("Response status must not be 206");
43-
}
44-
};
454

465
export interface CacheMetrics {
476
engine: string;
@@ -63,6 +22,8 @@ export const withInstrumentation = (
6322
const cacheImpl = await cache.open(cacheName);
6423
return {
6524
...cacheImpl,
25+
delete: cacheImpl.delete.bind(cacheImpl),
26+
put: cacheImpl.put.bind(cacheImpl),
6627
match: async (req, opts) => {
6728
const span = tracer.startSpan("cache-match", {
6829
attributes: { engine },

0 commit comments

Comments
 (0)