Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed revalidateTag writing to tags-manifest.json concurrently #52823

Closed
wants to merge 8 commits into from
2 changes: 2 additions & 0 deletions packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,9 @@ export default async function exportPage({
readFileSync: (f) => fs.readFileSync(f),
writeFile: (f, d) => fs.promises.writeFile(f, d),
mkdir: (dir) => fs.promises.mkdir(dir, { recursive: true }),
mkdirSync: (dir) => fs.mkdirSync(dir, { recursive: true }),
stat: (f) => fs.promises.stat(f),
createWriteStream: (f) => fs.createWriteStream(f),
},
serverDistDir: join(distDir, 'server'),
CurCacheHandler: CacheHandler,
Expand Down
52 changes: 31 additions & 21 deletions packages/next/src/server/lib/incremental-cache/file-system-cache.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { OutgoingHttpHeaders } from 'http'
import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './'
import LRUCache from 'next/dist/compiled/lru-cache'
import { CacheFs } from '../../../shared/lib/utils'

import path from '../../../shared/lib/isomorphic/path'
import { CacheFs } from '../../../shared/lib/utils'
import { CachedFetchValue } from '../../response-cache'
import { getDerivedTags } from './utils'
import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './'

type FileSystemCacheContext = Omit<
CacheHandlerContext,
Expand Down Expand Up @@ -59,11 +60,15 @@ export default class FileSystemCache implements CacheHandler {
})
}
if (this.serverDistDir && this.fs) {
this.tagsManifestPath = path.join(
const fetchCacheDirectory = path.join(
this.serverDistDir,
'..',
'cache',
'fetch-cache',
'fetch-cache'
)
this.fs.mkdirSync(fetchCacheDirectory)
this.tagsManifestPath = path.join(
fetchCacheDirectory,
'tags-manifest.json'
)
this.loadTagsManifest()
Expand Down Expand Up @@ -107,27 +112,32 @@ export default class FileSystemCache implements CacheHandler {
}

public async revalidateTag(tag: string) {
// we need to ensure the tagsManifest is refreshed
// since separate workers can be updating it at the same
// time and we can't flush out of sync data
this.loadTagsManifest()
if (!tagsManifest || !this.tagsManifestPath) {
if (!this.tagsManifestPath) {
return
}

const data = tagsManifest.items[tag] || { keys: [] }
data.revalidatedAt = Date.now()
tagsManifest.items[tag] = data
const manifestWriteStream = this.fs.createWriteStream(this.tagsManifestPath)

try {
await this.fs.mkdir(path.dirname(this.tagsManifestPath))
await this.fs.writeFile(
this.tagsManifestPath,
JSON.stringify(tagsManifest || {})
)
} catch (err: any) {
console.warn('Failed to update tags manifest.', err)
}
return new Promise<void>((resolve) => {
manifestWriteStream.on('ready', () => {
this.loadTagsManifest()
if (!tagsManifest) {
return
}
const data = tagsManifest.items[tag] || {
keys: [],
}
data.revalidatedAt = Date.now()
tagsManifest.items[tag] = data
manifestWriteStream.write(JSON.stringify(tagsManifest))
manifestWriteStream.end()
})
manifestWriteStream.on('finish', resolve)
manifestWriteStream.on('error', (err) => {
console.warn('Failed to update tags manifest.', err)
resolve()
})
})
}

public async get(key: string, fetchCache?: boolean) {
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/lib/node-fs-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ export const nodeFs: CacheFs = {
readFileSync: (f) => _fs.readFileSync(f),
writeFile: (f, d) => _fs.promises.writeFile(f, d),
mkdir: (dir) => _fs.promises.mkdir(dir, { recursive: true }),
mkdirSync: (dir) => _fs.mkdirSync(dir, { recursive: true }),
stat: (f) => _fs.promises.stat(f),
createWriteStream: (f) => _fs.createWriteStream(f),
}
14 changes: 9 additions & 5 deletions packages/next/src/shared/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { HtmlProps } from './html-context'
import type { ComponentType } from 'react'
import type { DomainLocale } from '../../server/config'
import type { Env } from '@next/env'
import type { WriteStream } from 'fs'
import type { IncomingMessage, ServerResponse } from 'http'
import type { NextRouter } from './router/router'
import type { ParsedUrlQuery } from 'querystring'
import type { PreviewData } from 'next/types'
import type { ParsedUrlQuery } from 'querystring'
import type { ComponentType } from 'react'

import type { DomainLocale } from '../../server/config'
import { COMPILER_NAMES } from './constants'
import type { HtmlProps } from './html-context'
import type { NextRouter } from './router/router'

export type NextComponentType<
Context extends BaseContext = NextPageContext,
Expand Down Expand Up @@ -450,7 +452,9 @@ export interface CacheFs {
readFileSync(f: string): Buffer
writeFile(f: string, d: any): Promise<void>
mkdir(dir: string): Promise<void | string>
mkdirSync(dir: string): string | undefined
stat(f: string): Promise<{ mtime: Date }>
createWriteStream(f: string): WriteStream
}

export function stringifyError(error: Error) {
Expand Down