diff --git a/packages/next/src/server/app-render/action-handler.ts b/packages/next/src/server/app-render/action-handler.ts index 5cf3c9e2c6fc9..26e45157e834e 100644 --- a/packages/next/src/server/app-render/action-handler.ts +++ b/packages/next/src/server/app-render/action-handler.ts @@ -113,9 +113,12 @@ async function addRevalidationHeader( requestStore: RequestStore } ) { - await Promise.all( - Object.values(staticGenerationStore.pendingRevalidates || []) - ) + await Promise.all([ + staticGenerationStore.incrementalCache?.revalidateTag( + staticGenerationStore.revalidatedTags || [] + ), + ...Object.values(staticGenerationStore.pendingRevalidates || {}), + ]) // If a tag was revalidated, the client router needs to invalidate all the // client router cache as they may be stale. And if a path was revalidated, the @@ -480,9 +483,12 @@ export async function handleAction({ if (isFetchAction) { res.statusCode = 500 - await Promise.all( - Object.values(staticGenerationStore.pendingRevalidates || []) - ) + await Promise.all([ + staticGenerationStore.incrementalCache?.revalidateTag( + staticGenerationStore.revalidatedTags || [] + ), + ...Object.values(staticGenerationStore.pendingRevalidates || {}), + ]) const promise = Promise.reject(error) try { @@ -867,9 +873,12 @@ export async function handleAction({ if (isFetchAction) { res.statusCode = 500 - await Promise.all( - Object.values(staticGenerationStore.pendingRevalidates || []) - ) + await Promise.all([ + staticGenerationStore.incrementalCache?.revalidateTag( + staticGenerationStore.revalidatedTags || [] + ), + ...Object.values(staticGenerationStore.pendingRevalidates || {}), + ]) const promise = Promise.reject(err) try { // we need to await the promise to trigger the rejection early diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 3a207806e4bea..3b92c350d3a40 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -1374,9 +1374,12 @@ async function renderToHTMLOrFlightImpl( // If we have pending revalidates, wait until they are all resolved. if (staticGenerationStore.pendingRevalidates) { - options.waitUntil = Promise.all( - Object.values(staticGenerationStore.pendingRevalidates) - ) + options.waitUntil = Promise.all([ + staticGenerationStore.incrementalCache?.revalidateTag( + staticGenerationStore.revalidatedTags || [] + ), + ...Object.values(staticGenerationStore.pendingRevalidates || {}), + ]) } addImplicitTags(staticGenerationStore) diff --git a/packages/next/src/server/future/route-modules/app-route/module.ts b/packages/next/src/server/future/route-modules/app-route/module.ts index dd362b38f21c3..10671285fa829 100644 --- a/packages/next/src/server/future/route-modules/app-route/module.ts +++ b/packages/next/src/server/future/route-modules/app-route/module.ts @@ -395,11 +395,14 @@ export class AppRouteRouteModule extends RouteModule< context.renderOpts.fetchMetrics = staticGenerationStore.fetchMetrics - context.renderOpts.waitUntil = Promise.all( - Object.values( - staticGenerationStore.pendingRevalidates || [] - ) - ) + context.renderOpts.waitUntil = Promise.all([ + staticGenerationStore.incrementalCache?.revalidateTag( + staticGenerationStore.revalidatedTags || [] + ), + ...Object.values( + staticGenerationStore.pendingRevalidates || {} + ), + ]) addImplicitTags(staticGenerationStore) ;(context.renderOpts as any).fetchTags = diff --git a/packages/next/src/server/lib/incremental-cache/fetch-cache.ts b/packages/next/src/server/lib/incremental-cache/fetch-cache.ts index 6b89c97ed2f8a..09ffa43102acc 100644 --- a/packages/next/src/server/lib/incremental-cache/fetch-cache.ts +++ b/packages/next/src/server/lib/incremental-cache/fetch-cache.ts @@ -126,11 +126,17 @@ export default class FetchCache implements CacheHandler { memoryCache?.reset() } - public async revalidateTag(tag: string) { + public async revalidateTag( + ...args: Parameters + ) { + let [tags] = args + tags = typeof tags === 'string' ? [tags] : tags if (this.debug) { - console.log('revalidateTag', tag) + console.log('revalidateTag', tags) } + if (!tags.length) return + if (Date.now() < rateLimitedUntil) { if (this.debug) { console.log('rate limited ', rateLimitedUntil) @@ -140,9 +146,9 @@ export default class FetchCache implements CacheHandler { try { const res = await fetch( - `${ - this.cacheEndpoint - }/v1/suspense-cache/revalidate?tags=${encodeURIComponent(tag)}`, + `${this.cacheEndpoint}/v1/suspense-cache/revalidate?tags=${tags + .map((tag) => encodeURIComponent(tag)) + .join(',')}`, { method: 'POST', headers: this.headers, @@ -160,7 +166,7 @@ export default class FetchCache implements CacheHandler { throw new Error(`Request failed with status ${res.status}.`) } } catch (err) { - console.warn(`Failed to revalidate tag ${tag}`, err) + console.warn(`Failed to revalidate tag ${tags}`, err) } } diff --git a/packages/next/src/server/lib/incremental-cache/file-system-cache.ts b/packages/next/src/server/lib/incremental-cache/file-system-cache.ts index 9098413e0a8a3..c1a7c3ef5bf20 100644 --- a/packages/next/src/server/lib/incremental-cache/file-system-cache.ts +++ b/packages/next/src/server/lib/incremental-cache/file-system-cache.ts @@ -105,9 +105,18 @@ export default class FileSystemCache implements CacheHandler { if (this.debug) console.log('loadTagsManifest', tagsManifest) } - public async revalidateTag(tag: string) { + public async revalidateTag( + ...args: Parameters + ) { + let [tags] = args + tags = typeof tags === 'string' ? [tags] : tags + if (this.debug) { - console.log('revalidateTag', tag) + console.log('revalidateTag', tags) + } + + if (tags.length === 0) { + return } // we need to ensure the tagsManifest is refreshed @@ -118,9 +127,11 @@ export default class FileSystemCache implements CacheHandler { return } - const data = tagsManifest.items[tag] || {} - data.revalidatedAt = Date.now() - tagsManifest.items[tag] = data + for (const tag of tags) { + const data = tagsManifest.items[tag] || {} + data.revalidatedAt = Date.now() + tagsManifest.items[tag] = data + } try { await this.fs.mkdir(path.dirname(this.tagsManifestPath)) diff --git a/packages/next/src/server/lib/incremental-cache/index.ts b/packages/next/src/server/lib/incremental-cache/index.ts index 7cf738ae2d6a2..021ec29344d33 100644 --- a/packages/next/src/server/lib/incremental-cache/index.ts +++ b/packages/next/src/server/lib/incremental-cache/index.ts @@ -58,7 +58,9 @@ export class CacheHandler { ..._args: Parameters ): Promise {} - public async revalidateTag(_tag: string): Promise {} + public async revalidateTag( + ..._args: Parameters + ): Promise {} public resetRequestCache(): void {} } @@ -280,7 +282,7 @@ export class IncrementalCache implements IncrementalCacheType { return unlockNext } - async revalidateTag(tag: string) { + async revalidateTag(tags: string | string[]): Promise { if ( process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT && process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY && @@ -296,7 +298,7 @@ export class IncrementalCache implements IncrementalCacheType { }) } - return this.cacheHandler?.revalidateTag?.(tag) + return this.cacheHandler?.revalidateTag?.(tags) } // x-ref: https://github.com/facebook/react/blob/2655c9354d8e1c54ba888444220f63e836925caa/packages/react/src/ReactFetch.js#L23 diff --git a/packages/next/src/server/web/spec-extension/revalidate.ts b/packages/next/src/server/web/spec-extension/revalidate.ts index ef10e466fe07f..00996c7d188f2 100644 --- a/packages/next/src/server/web/spec-extension/revalidate.ts +++ b/packages/next/src/server/web/spec-extension/revalidate.ts @@ -68,15 +68,6 @@ function revalidate(tag: string, expression: string) { store.revalidatedTags.push(tag) } - if (!store.pendingRevalidates) { - store.pendingRevalidates = {} - } - store.pendingRevalidates[tag] = store.incrementalCache - .revalidateTag?.(tag) - .catch((err) => { - console.error(`revalidate failed for ${tag}`, err) - }) - // TODO: only revalidate if the path matches store.pathWasRevalidated = true }