8000 Update tags handling during server action redirect (#49227) · vercel/next.js@0bf6c27 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0bf6c27

Browse files
authored
Update tags handling during server action redirect (#49227)
Updates the tag handling during a server action direct to pass through the revalidated tags for immediate revalidation as discussed. x-ref: [slack thread](https://vercel.slack.com/archives/C042LHPJ1NX/p1683218335368759)
1 parent bf49f62 commit 0bf6c27

File tree

8 files changed

+85
-27
lines changed

8 files changed

+85
-27
lines changed

packages/next/src/client/components/static-generation-async-storage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export interface StaticGenerationStore {
3131
pathWasRevalidated?: boolean
3232

3333
tags?: string[]
34+
35+
revalidatedTags?: string[]
3436
fetchMetrics?: Array<{
3537
url: string
3638
idx: number

packages/next/src/server/app-render/action-handler.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,18 @@ async function createRedirectRenderResult(
142142
staticGenerationStore.incrementalCache?.requestProtocol || 'https'
143143
const fetchUrl = new URL(`${proto}://${host}${redirectUrl}`)
144144

145+
if (staticGenerationStore.revalidatedTags) {
146+
forwardedHeaders.set(
147+
'x-next-revalidated-tags',
148+
staticGenerationStore.revalidatedTags.join(',')
149+
)
150+
forwardedHeaders.set(
151+
'x-next-revalidate-tag-token',
152+
staticGenerationStore.incrementalCache?.prerenderManifest?.preview
153+
?.previewModeId || ''
154+
)
155+
}
156+
145157
try {
146158
const headResponse = await fetchIPv4v6(fetchUrl, {
147159
method: 'HEAD',

packages/next/src/server/lib/incremental-cache/fetch-cache.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import LRUCache from 'next/dist/compiled/lru-cache'
22
import { FETCH_CACHE_HEADER } from '../../../client/components/app-router-headers'
33
import { CACHE_ONE_YEAR } from '../../../lib/constants'
44
import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './'
5+
import { getDerivedTags } from './utils'
56

67
let memoryCache: LRUCache<string, CacheHandlerValue> | undefined
78

@@ -16,10 +17,12 @@ export default class FetchCache implements CacheHandler {
1617
private headers: Record<string, string>
1718
private cacheEndpoint?: string
1819
private debug: boolean
20+
private revalidatedTags: string[]
1921

2022
constructor(ctx: CacheHandlerContext) {
2123
this.debug = !!process.env.NEXT_PRIVATE_DEBUG_CACHE
2224
this.headers = {}
25+
this.revalidatedTags = ctx.revalidatedTags
2326
this.headers['Content-Type'] = 'application/json'
2427

2528
if (FETCH_CACHE_HEADER in ctx._requestHeaders) {
@@ -189,6 +192,21 @@ export default class FetchCache implements CacheHandler {
189192
}
190193
}
191194
}
195+
196+
// if a tag was revalidated we don't return stale data
197+
if (data?.value?.kind === 'FETCH') {
198+
const innerData = data.value.data
199+
const derivedTags = getDerivedTags(innerData.tags || [])
200+
201+
if (
202+
derivedTags.some((tag) => {
203+
return this.revalidatedTags.includes(tag)
204+
})
205+
) {
206+
data = undefined
207+
}
208+
}
209+
192210
return data || null
193211
}
194212

packages/next/src/server/lib/incremental-cache/file-system-cache.ts

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import LRUCache from 'next/dist/compiled/lru-cache'
44
import { CacheFs } from '../../../shared/lib/utils'
55
import path from '../../../shared/lib/isomorphic/path'
66
import { CachedFetchValue } from '../../response-cache'
7+
import { getDerivedTags } from './utils'
78

89
type FileSystemCacheContext = Omit<
910
CacheHandlerContext,
@@ -26,12 +27,14 @@ export default class FileSystemCache implements CacheHandler {
2627
private serverDistDir: FileSystemCacheContext['serverDistDir']
2728
private appDir: boolean
2829
private tagsManifestPath?: string
30+
private revalidatedTags: string[]
2931

3032
constructor(ctx: FileSystemCacheContext) {
3133
this.fs = ctx.fs
3234
this.flushToDisk = ctx.flushToDisk
3335
this.serverDistDir = ctx.serverDistDir
3436
this.appDir = !!ctx._appDir
37+
this.revalidatedTags = ctx.revalidatedTags
3538

3639
if (ctx.maxMemoryCacheSize && !memoryCache) {
3740
memoryCache = new LRUCache({
@@ -241,33 +244,6 @@ export default class FileSystemCache implements CacheHandler {
241244
}
242245
}
243246

244-
const getDerivedTags = (tags: string[]): string[] => {
245-
const derivedTags: string[] = ['/']
246-
247-
for (const tag of tags || []) {
248-
if (tag.startsWith('/')) {
249-
const pathnameParts = tag.split('/')
250-
251-
// we automatically add the current path segments as tags
252-
// for revalidatePath handling
253-
for (let i = 1; i < pathnameParts.length + 1; i++) {
254-
const curPathname = pathnameParts.slice(0, i).join('/')
255-
256-
if (curPathname) {
257-
derivedTags.push(curPathname)
258-
259-
if (!derivedTags.includes(curPathname)) {
260-
derivedTags.push(curPathname)
261-
}
262-
}
263-
}
264-
} else if (!derivedTags.includes(tag)) {
265-
derivedTags.push(tag)
266-
}
267-
}
268-
return derivedTags
269-
}
270-
271247
if (data?.value?.kind === 'PAGE' && cacheTags?.length) {
272248
this.loadTagsManifest()
273249
const derivedTags = getDerivedTags(cacheTags || [])
@@ -290,6 +266,10 @@ export default class FileSystemCache implements CacheHandler {
290266
const derivedTags = getDerivedTags(innerData.tags || [])
291267

292268
const wasRevalidated = derivedTags.some((tag) => {
269+
if (this.revalidatedTags.includes(tag)) {
270+
return true
271+
}
272+
293273
return (
294274
tagsManifest?.items[tag]?.revalidatedAt &&
295275
tagsManifest?.items[tag].revalidatedAt >=

packages/next/src/server/lib/incremental-cache/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface CacheHandlerContext {
2424
maxMemoryCacheSize?: number
2525
fetchCacheKeyPrefix?: string
2626
prerenderManifest?: PrerenderManifest
27+
revalidatedTags: string[]
2728
_appDir: boolean
2829
_requestHeaders: IncrementalCache['requestHeaders']
2930
}
@@ -68,6 +69,7 @@ export class IncrementalCache {
6869
allowedRevalidateHeaderKeys?: string[]
6970
minimalMode?: boolean
7071
fetchCacheKeyPrefix?: string
72+
revalidatedTags?: string[]
7173

7274
constructor({
7375
fs,
@@ -120,13 +122,24 @@ export class IncrementalCache {
120122
this.allowedRevalidateHeaderKeys = allowedRevalidateHeaderKeys
121123
this.prerenderManifest = getPrerenderManifest()
122124
this.fetchCacheKeyPrefix = fetchCacheKeyPrefix
125+
let revalidatedTags: string[] = []
126+
127+
if (
128+
minimalMode &&
129+
typeof requestHeaders['x-next-revalidated-tags'] === 'string' &&
130+
requestHeaders['x-next-revalidats-tag-token'] ===
131+
this.prerenderManifest?.preview?.previewModeId
132+
) {
133+
revalidatedTags = requestHeaders['x-next-revalidated-tags'].split(',')
134+
}
123135

124136
if (CurCacheHandler) {
125137
this.cacheHandler = new CurCacheHandler({
126138
dev,
127139
fs,
128140
flushToDisk,
129141
serverDistDir,
142+
revalidatedTags,
130143
maxMemoryCacheSize,
131144
_appDir: !!appDir,
132145
_requestHeaders: requestHeaders,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export const getDerivedTags = (tags: string[]): string[] => {
2+
const derivedTags: string[] = ['/']
3+
4+
for (const tag of tags || []) {
5+
if (tag.startsWith('/')) {
6+
const pathnameParts = tag.split('/')
7+
8+
// we automatically add the current path segments as tags
9+
// for revalidatePath handling
10+
for (let i = 1; i < pathnameParts.length + 1; i++) {
11+
const curPathname = pathnameParts.slice(0, i).join('/')
12+
13+
if (curPathname) {
14+
derivedTags.push(curPathname)
15+
16+
if (!derivedTags.includes(curPathname)) {
17+
derivedTags.push(curPathname)
18+
}
19+
}
20+
}
21+
} else if (!derivedTags.includes(tag)) {
22+
derivedTags.push(tag)
23+
}
24+
}
25+
return derivedTags
26+
}

packages/next/src/server/web/spec-extension/revalidate-tag.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export function revalidateTag(tag: string) {
1616
`Invariant: static generation store missing in revalidateTag ${tag}`
1717
)
1818
}
19+
if (!store.revalidatedTags) {
20+
store.revalidatedTags = []
21+
}
22+
if (!store.revalidatedTags.includes(tag)) {
23+
store.revalidatedTags.push(tag)
24+
}
1925

2026
if (!store.pendingRevalidates) {
2127
store.pendingRevalidates = []

test/unit/incremental-cache/file-system-cache.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('FileSystemCache', () => {
1313
flushToDisk: true,
1414
fs: nodeFs,
1515
serverDistDir: cacheDir,
16+
revalidatedTags: [],
1617
})
1718

1819
const binary = await fs.readFile(

0 commit comments

Comments
 (0)
0