Skip to content

Commit

Permalink
Update tags handling during server action redirect (#49227)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
ijjk authored May 4, 2023
1 parent bf49f62 commit 0bf6c27
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export interface StaticGenerationStore {
pathWasRevalidated?: boolean

tags?: string[]

revalidatedTags?: string[]
fetchMetrics?: Array<{
url: string
idx: number
Expand Down
12 changes: 12 additions & 0 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ async function createRedirectRenderResult(
staticGenerationStore.incrementalCache?.requestProtocol || 'https'
const fetchUrl = new URL(`${proto}://${host}${redirectUrl}`)

if (staticGenerationStore.revalidatedTags) {
forwardedHeaders.set(
'x-next-revalidated-tags',
staticGenerationStore.revalidatedTags.join(',')
)
forwardedHeaders.set(
'x-next-revalidate-tag-token',
staticGenerationStore.incrementalCache?.prerenderManifest?.preview
?.previewModeId || ''
)
}

try {
const headResponse = await fetchIPv4v6(fetchUrl, {
method: 'HEAD',
Expand Down
18 changes: 18 additions & 0 deletions packages/next/src/server/lib/incremental-cache/fetch-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import LRUCache from 'next/dist/compiled/lru-cache'
import { FETCH_CACHE_HEADER } from '../../../client/components/app-router-headers'
import { CACHE_ONE_YEAR } from '../../../lib/constants'
import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './'
import { getDerivedTags } from './utils'

let memoryCache: LRUCache<string, CacheHandlerValue> | undefined

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

constructor(ctx: CacheHandlerContext) {
this.debug = !!process.env.NEXT_PRIVATE_DEBUG_CACHE
this.headers = {}
this.revalidatedTags = ctx.revalidatedTags
this.headers['Content-Type'] = 'application/json'

if (FETCH_CACHE_HEADER in ctx._requestHeaders) {
Expand Down Expand Up @@ -189,6 +192,21 @@ export default class FetchCache implements CacheHandler {
}
}
}

// if a tag was revalidated we don't return stale data
if (data?.value?.kind === 'FETCH') {
const innerData = data.value.data
const derivedTags = getDerivedTags(innerData.tags || [])

if (
derivedTags.some((tag) => {
return this.revalidatedTags.includes(tag)
})
) {
data = undefined
}
}

return data || null
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LRUCache from 'next/dist/compiled/lru-cache'
import { CacheFs } from '../../../shared/lib/utils'
import path from '../../../shared/lib/isomorphic/path'
import { CachedFetchValue } from '../../response-cache'
import { getDerivedTags } from './utils'

type FileSystemCacheContext = Omit<
CacheHandlerContext,
Expand All @@ -26,12 +27,14 @@ export default class FileSystemCache implements CacheHandler {
private serverDistDir: FileSystemCacheContext['serverDistDir']
private appDir: boolean
private tagsManifestPath?: string
private revalidatedTags: string[]

constructor(ctx: FileSystemCacheContext) {
this.fs = ctx.fs
this.flushToDisk = ctx.flushToDisk
this.serverDistDir = ctx.serverDistDir
this.appDir = !!ctx._appDir
this.revalidatedTags = ctx.revalidatedTags

if (ctx.maxMemoryCacheSize && !memoryCache) {
memoryCache = new LRUCache({
Expand Down Expand Up @@ -241,33 +244,6 @@ export default class FileSystemCache implements CacheHandler {
}
}

const getDerivedTags = (tags: string[]): string[] => {
const derivedTags: string[] = ['/']

for (const tag of tags || []) {
if (tag.startsWith('/')) {
const pathnameParts = tag.split('/')

// we automatically add the current path segments as tags
// for revalidatePath handling
for (let i = 1; i < pathnameParts.length + 1; i++) {
const curPathname = pathnameParts.slice(0, i).join('/')

if (curPathname) {
derivedTags.push(curPathname)

if (!derivedTags.includes(curPathname)) {
derivedTags.push(curPathname)
}
}
}
} else if (!derivedTags.includes(tag)) {
derivedTags.push(tag)
}
}
return derivedTags
}

if (data?.value?.kind === 'PAGE' && cacheTags?.length) {
this.loadTagsManifest()
const derivedTags = getDerivedTags(cacheTags || [])
Expand All @@ -290,6 +266,10 @@ export default class FileSystemCache implements CacheHandler {
const derivedTags = getDerivedTags(innerData.tags || [])

const wasRevalidated = derivedTags.some((tag) => {
if (this.revalidatedTags.includes(tag)) {
return true
}

return (
tagsManifest?.items[tag]?.revalidatedAt &&
tagsManifest?.items[tag].revalidatedAt >=
Expand Down
13 changes: 13 additions & 0 deletions packages/next/src/server/lib/incremental-cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface CacheHandlerContext {
maxMemoryCacheSize?: number
fetchCacheKeyPrefix?: string
prerenderManifest?: PrerenderManifest
revalidatedTags: string[]
_appDir: boolean
_requestHeaders: IncrementalCache['requestHeaders']
}
Expand Down Expand Up @@ -68,6 +69,7 @@ export class IncrementalCache {
allowedRevalidateHeaderKeys?: string[]
minimalMode?: boolean
fetchCacheKeyPrefix?: string
revalidatedTags?: string[]

constructor({
fs,
Expand Down Expand Up @@ -120,13 +122,24 @@ export class IncrementalCache {
this.allowedRevalidateHeaderKeys = allowedRevalidateHeaderKeys
this.prerenderManifest = getPrerenderManifest()
this.fetchCacheKeyPrefix = fetchCacheKeyPrefix
let revalidatedTags: string[] = []

if (
minimalMode &&
typeof requestHeaders['x-next-revalidated-tags'] === 'string' &&
requestHeaders['x-next-revalidats-tag-token'] ===
this.prerenderManifest?.preview?.previewModeId
) {
revalidatedTags = requestHeaders['x-next-revalidated-tags'].split(',')
}

if (CurCacheHandler) {
this.cacheHandler = new CurCacheHandler({
dev,
fs,
flushToDisk,
serverDistDir,
revalidatedTags,
maxMemoryCacheSize,
_appDir: !!appDir,
_requestHeaders: requestHeaders,
Expand Down
26 changes: 26 additions & 0 deletions packages/next/src/server/lib/incremental-cache/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const getDerivedTags = (tags: string[]): string[] => {
const derivedTags: string[] = ['/']

for (const tag of tags || []) {
if (tag.startsWith('/')) {
const pathnameParts = tag.split('/')

// we automatically add the current path segments as tags
// for revalidatePath handling
for (let i = 1; i < pathnameParts.length + 1; i++) {
const curPathname = pathnameParts.slice(0, i).join('/')

if (curPathname) {
derivedTags.push(curPathname)

if (!derivedTags.includes(curPathname)) {
derivedTags.push(curPathname)
}
}
}
} else if (!derivedTags.includes(tag)) {
derivedTags.push(tag)
}
}
return derivedTags
}
6 changes: 6 additions & 0 deletions packages/next/src/server/web/spec-extension/revalidate-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export function revalidateTag(tag: string) {
`Invariant: static generation store missing in revalidateTag ${tag}`
)
}
if (!store.revalidatedTags) {
store.revalidatedTags = []
}
if (!store.revalidatedTags.includes(tag)) {
store.revalidatedTags.push(tag)
}

if (!store.pendingRevalidates) {
store.pendingRevalidates = []
Expand Down
1 change: 1 addition & 0 deletions test/unit/incremental-cache/file-system-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('FileSystemCache', () => {
flushToDisk: true,
fs: nodeFs,
serverDistDir: cacheDir,
revalidatedTags: [],
})

const binary = await fs.readFile(
Expand Down

0 comments on commit 0bf6c27

Please sign in to comment.