Skip to content

Commit 51dcbe0

Browse files
authoredFeb 20, 2024
fix: use response cache key for tag manifest first before falling back to pathname (#280)
* test: add i18n+basePath page router fixture, add tests for cache-tag correctness * fix: use response cache key for tag manifest first before falling back to pathname * fix: mark request as on-demand revalidate so we purge cdn cache tag after updating response cache
1 parent 3d7b20f commit 51dcbe0

15 files changed

+1269
-139
lines changed
 

‎src/run/handlers/cache.cts

+12
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,18 @@ export class NetlifyCacheHandler implements CacheHandler {
177177
lastModified,
178178
value: data,
179179
})
180+
181+
if (data?.kind === 'PAGE') {
182+
const requestContext = getRequestContext()
183+
if (requestContext?.didPagesRouterOnDemandRevalidate) {
184+
const tag = `_N_T_${key === '/index' ? '/' : key}`
185+
console.debug('Purging CDN cache for:', [tag])
186+
purgeCache({ tags: [tag] }).catch((error) => {
187+
// TODO: add reporting here
188+
console.error(`[NetlifyCacheHandler]: Purging the cache for tag ${tag} failed`, error)
189+
})
190+
}
191+
}
180192
span.end()
181193
})
182194
}

‎src/run/handlers/request-context.cts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type RequestContext = {
44
responseCacheGetLastModified?: number
55
responseCacheKey?: string
66
usedFsRead?: boolean
7+
didPagesRouterOnDemandRevalidate?: boolean
78
}
89

910
type RequestContextAsyncLocalStorage = AsyncLocalStorage<RequestContext>

‎src/run/handlers/server.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ export default async (request: Request) => {
5151
return await tracer.startActiveSpan('generate response', async (span) => {
5252
const { req, res } = toReqRes(request)
5353

54-
const resProxy = nextResponseProxy(res)
55-
5654
const requestContext = createRequestContext()
5755

56+
const resProxy = nextResponseProxy(res, requestContext)
57+
5858
// We don't await this here, because it won't resolve until the response is finished.
5959
const nextHandlerPromise = runWithRequestContext(requestContext, () =>
6060
nextHandler(req, resProxy).catch((error) => {
@@ -83,7 +83,7 @@ export default async (request: Request) => {
8383
await adjustDateHeader({ headers: response.headers, request, span, tracer, requestContext })
8484

8585
setCacheControlHeaders(response.headers, request, requestContext)
86-
setCacheTagsHeaders(response.headers, request, tagsManifest)
86+
setCacheTagsHeaders(response.headers, request, tagsManifest, requestContext)
8787
setVaryHeaders(response.headers, request, nextConfig)
8888
setCacheStatusHeader(response.headers)
8989

‎src/run/headers.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,18 @@ export const setCacheControlHeaders = (
213213
}
214214
}
215215

216-
export const setCacheTagsHeaders = (headers: Headers, request: Request, manifest: TagsManifest) => {
217-
const path = new URL(request.url).pathname
216+
function getCanonicalPathFromCacheKey(cacheKey: string | undefined): string | undefined {
217+
return cacheKey === '/index' ? '/' : cacheKey
218+
}
219+
220+
export const setCacheTagsHeaders = (
221+
headers: Headers,
222+
request: Request,
223+
manifest: TagsManifest,
224+
requestContext: RequestContext,
225+
) => {
226+
const path =
227+
getCanonicalPathFromCacheKey(requestContext.responseCacheKey) ?? new URL(request.url).pathname
218228
const tags = manifest[path]
219229
if (tags !== undefined) {
220230
headers.set('netlify-cache-tag', tags)

‎src/run/revalidate.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
11
import type { ServerResponse } from 'node:http'
22

3-
import { purgeCache } from '@netlify/functions'
3+
import type { RequestContext } from './handlers/request-context.cjs'
44

55
// Needing to proxy the response object to intercept the revalidate call for on-demand revalidation on page routes
6-
export const nextResponseProxy = (res: ServerResponse) => {
6+
export const nextResponseProxy = (res: ServerResponse, requestContext: RequestContext) => {
77
return new Proxy(res, {
88
// eslint-disable-next-line @typescript-eslint/no-explicit-any
99
get(target: any[string], key: string) {
1010
const originalValue = target[key]
1111
if (key === 'revalidate') {
1212
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1313
return async function newRevalidate(...args: any[]) {
14-
try {
15-
console.debug('Purging cache for:', [args[0]])
16-
await purgeCache({ tags: [`_N_T_${args[0]}`] })
17-
} catch {
18-
throw new Error(
19-
`An internal error occurred while trying to purge cache for ${args[0]}}`,
20-
)
21-
}
14+
requestContext.didPagesRouterOnDemandRevalidate = true
2215
return originalValue?.apply(target, args)
2316
}
2417
}

0 commit comments

Comments
 (0)
Please sign in to comment.