From cede9878917bbb78146b74094c131663e2517937 Mon Sep 17 00:00:00 2001 From: Daniel Lamando Date: Mon, 23 Sep 2024 22:40:53 +0200 Subject: [PATCH] Add Deno platform cache to codegen service (#47) * Add Deno platform cache to codegen service * register cache differently? * Another attempt at edge caching * Redo platform cache fetch * add debug event * Hmm so we need to do cache versioning? * Add tracing event for cache hit failure * Ok, maybe need to x- the header * Remove debug tracing event --- generation/deploy/cache-platform.ts | 65 +++++++++++++++++++++++++++++ generation/deploy/cache.ts | 14 ++++--- 2 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 generation/deploy/cache-platform.ts diff --git a/generation/deploy/cache-platform.ts b/generation/deploy/cache-platform.ts new file mode 100644 index 0000000..be53f83 --- /dev/null +++ b/generation/deploy/cache-platform.ts @@ -0,0 +1,65 @@ +import { Cache } from "https://deno.land/x/httpcache@0.1.2/mod.ts"; +import { trace } from "./tracer.ts"; + +// https://docs.deno.com/deploy/manual/edge-cache + +function transformUrl(url: string) { + return url.replace(/\/\/[^\/]+\//, '//upstream-cache-v2/'); +} + +export async function platformCache( + name = 'http-policy-cache', +): Promise { + + const nativeCache = await caches.open(name); + + return new Cache({ + + async get(url) { + const cached = await nativeCache.match(transformUrl(url)); + if (!cached) return undefined; + + type CachePolicy = Parameters[0]['set']>[1]['policy']; + + const { + 'x-cache-policy': policyHeader, + ...resh + } = Object.fromEntries(cached.headers.entries()); + + if (!policyHeader) { + trace.getActiveSpan()?.addEvent('edgecache.fail', { + 'message': 'platform cache hit lacked policy', + }); + console.warn('WARN: platform cache lacked policy', cached); + return undefined; + } + const policy = { + ...JSON.parse(policyHeader), + resh, + } as CachePolicy; + + const BodyBuffer = new Uint8Array(await cached.arrayBuffer()); + + return { + policy, + body: BodyBuffer, + }; + }, + + async set(url, resp) { + const {resh, ...rest} = resp.policy; + await nativeCache.put(transformUrl(url), new Response(resp.body, { + headers: { + ...resh, + 'x-cache-policy': JSON.stringify(rest), + }, + })); + }, + + async delete(url) { + await nativeCache.delete(transformUrl(url)); + }, + + close() {}, + }); +} diff --git a/generation/deploy/cache.ts b/generation/deploy/cache.ts index 9479a81..4386cff 100644 --- a/generation/deploy/cache.ts +++ b/generation/deploy/cache.ts @@ -1,8 +1,12 @@ -import { inMemoryCache } from "https://deno.land/x/httpcache@0.1.2/in_memory.ts"; -import type { Cache } from "https://deno.land/x/httpcache@0.1.2/mod.ts"; -import { s3Cache } from "./cache-s3.ts"; import { S3 } from "./s3-api.ts"; import { ApiFactory } from "../../lib/client/mod.ts"; +import { AsyncTracer, Span } from "./tracer.ts"; + +// can maybe replace this whole dep with deno edge cache once it's out of beta +import type { Cache } from "https://deno.land/x/httpcache@0.1.2/mod.ts"; +import { inMemoryCache } from "https://deno.land/x/httpcache@0.1.2/in_memory.ts"; +import { s3Cache } from "./cache-s3.ts"; +import { platformCache } from "./cache-platform.ts"; const s3Api = new ApiFactory({ region: Deno.env.get('HTTPCACHE_S3_REGION') || 'us-east-2', @@ -10,11 +14,11 @@ const s3Api = new ApiFactory({ const caches: Array = [ inMemoryCache(40), + await platformCache(), s3Cache(s3Api, Deno.env.get('HTTPCACHE_S3_BUCKET') || 'deno-httpcache'), ]; -const cacheLabels = ['in-memory', 's3']; +const cacheLabels = ['in-memory', 'platform', 's3']; -import { AsyncTracer, Span } from "./tracer.ts"; const tracer = new AsyncTracer('cached-fetch'); export async function cachedFetch(mode: 'immutable' | 'mutable', label: string, url: string) {