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

Update fetch cache memory handling #47465

Merged
merged 11 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/trigger_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ jobs:
- run: pnpm run build

- run: node ./scripts/start-release.js --release-type ${{ github.event.inputs.releaseType }} --semver-type ${{ github.event.inputs.semverType }}
env:
START_RELEASE_TOKEN: ${{ secrets.START_RELEASE_TOKEN }}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export interface StaticGenerationStore {
readonly isPrerendering?: boolean

forceDynamic?: boolean
fetchCache?:
| 'only-cache'
| 'force-cache'
| 'force-no-store'
| 'default-no-store'
| 'only-no-store'
revalidate?: false | number
forceStatic?: boolean
dynamicShouldError?: boolean
Expand Down
4 changes: 4 additions & 0 deletions packages/next/src/server/app-render/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,10 @@ export async function renderToHTMLOrFlight(
}
}

if (typeof layoutOrPageMod?.fetchCache === 'string') {
staticGenerationStore.fetchCache = layoutOrPageMod?.fetchCache
}

if (typeof layoutOrPageMod?.revalidate === 'number') {
defaultRevalidate = layoutOrPageMod.revalidate as number

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type RequestContext = {
isRevalidate?: boolean
isBot?: boolean
nextExport?: boolean
fetchCache?: StaticGenerationStore['fetchCache']
}
}

Expand Down Expand Up @@ -55,6 +56,7 @@ export class StaticGenerationAsyncStorageWrapper
incrementalCache: renderOpts.incrementalCache,
isRevalidate: renderOpts.isRevalidate,
isPrerendering: renderOpts.nextExport,
fetchCache: renderOpts.fetchCache,
}
;(renderOpts as any).store = store

Expand Down
13 changes: 10 additions & 3 deletions packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,15 @@ export default abstract class Server<ServerOptions extends Options = Options> {
if (!headers['content-type'] && blob.type) {
headers['content-type'] = blob.type
}
let revalidate: number | false | undefined = (
(context as any).store as any as
| { revalidate?: number }
| undefined
)?.revalidate

if (typeof revalidate == 'undefined') {
revalidate = false
}

// Create the cache entry for the response.
const cacheEntry: ResponseCacheEntry = {
Expand All @@ -1494,9 +1503,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
body: Buffer.from(await blob.arrayBuffer()),
headers,
},
revalidate:
((context as any).store as any as { revalidate?: number })
.revalidate || false,
revalidate,
}

return cacheEntry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import type { ModuleLoader } from '../helpers/module-loader/module-loader'
import { RouteHandler } from './route-handler'
import * as Log from '../../../build/output/log'
import { patchFetch } from '../../lib/patch-fetch'
import { StaticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage'
import {
StaticGenerationAsyncStorage,
StaticGenerationStore,
} from '../../../client/components/static-generation-async-storage'
import { StaticGenerationAsyncStorageWrapper } from '../../async-storage/static-generation-async-storage-wrapper'
import { IncrementalCache } from '../../lib/incremental-cache'
import { AppConfig } from '../../../build/utils'
Expand Down Expand Up @@ -74,7 +77,8 @@ export type AppRouteModule = {
*/
handlers: Record<HTTP_METHOD, AppRouteHandlerFn> &
Record<'dynamic', AppConfig['dynamic']> &
Record<'revalidate', AppConfig['revalidate']>
Record<'revalidate', AppConfig['revalidate']> &
Record<'fetchCache', AppConfig['fetchCache']>

/**
* The exported async storage object for this worker/module.
Expand All @@ -99,6 +103,7 @@ export type StaticGenerationContext = {
incrementalCache?: IncrementalCache
supportsDynamicHTML: boolean
nextExport?: boolean
fetchCache?: StaticGenerationStore['fetchCache']
}

/**
Expand Down Expand Up @@ -473,6 +478,10 @@ export class AppRouteRouteHandler implements RouteHandler<AppRouteRouteMatch> {
res: (res as NodeNextResponse).originalResponse,
}

if (context) {
context.fetchCache = module.handlers.fetchCache
}

// Run the handler with the request AsyncLocalStorage to inject the helper
// support.
const response = await this.requestAsyncLocalStorageWrapper.wrap(
Expand All @@ -485,6 +494,7 @@ export class AppRouteRouteHandler implements RouteHandler<AppRouteRouteMatch> {
pathname: definition.pathname,
renderOpts: context ?? {
supportsDynamicHTML: false,
fetchCache: module.handlers.fetchCache,
},
},
(staticGenerationStore) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ export default class FetchCache implements CacheHandler {

let data = memoryCache?.get(key)

// memory cache data is only leveraged for up to 1 seconds
// so that revalidation events can be pulled from source
if (Date.now() - (data?.lastModified || 0) > 2000) {
data = undefined
}

// get data from fetch cache
if (!data && this.cacheEndpoint) {
try {
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/server/lib/incremental-cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,8 @@ export class IncrementalCache {
fetchCache?: boolean
) {
if (this.dev && !fetchCache) return
// fetchCache has upper limit of 1MB per-entry currently
if (fetchCache && JSON.stringify(data).length > 1024 * 1024) {
// fetchCache has upper limit of 2MB per-entry currently
if (fetchCache && JSON.stringify(data).length > 2 * 1024 * 1024) {
if (this.dev) {
throw new Error(`fetch for over 1MB of data can not be cached`)
}
Expand Down
9 changes: 9 additions & 0 deletions packages/next/src/server/lib/patch-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ export function patchFetch({
? _headers
: new Headers(_headers || {})

// const isOnlyCache = staticGenerationStore.fetchCache === 'only-cache'
// const isForceCache = staticGenerationStore.fetchCache === 'force-cache'
// const isDefaultNoStore =
// staticGenerationStore.fetchCache === 'default-no-store'
// const isOnlyNoStore =
// staticGenerationStore.fetchCache === 'only-no-store'
// const isForceNoStore =
// staticGenerationStore.fetchCache === 'force-no-store'

const hasUnCacheableHeader =
initHeaders.get('authorization') || initHeaders.get('cookie')

Expand Down
2 changes: 1 addition & 1 deletion scripts/start-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ async function main() {
const args = process.argv
const releaseType = args[args.indexOf('--release-type') + 1]
const semverType = args[args.indexOf('--semver-type') + 1]
const isCanary = releaseType === 'canary'
const isCanary = releaseType !== 'stable'

if (releaseType !== 'stable' && releaseType !== 'canary') {
console.log(`Invalid release type ${releaseType}, must be stable or canary`)
Expand Down
40 changes: 26 additions & 14 deletions test/integration/next-image-new/app-dir/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,32 @@ function runTests(mode) {
const imagesizes = await link.getAttribute('imagesizes')
entries.push({ fetchpriority, imagesrcset, imagesizes })
}
expect(entries).toEqual([
{
fetchpriority: 'high',
imagesizes: '',
imagesrcset:
'/_next/image?url=%2Ftest.jpg&w=640&q=75 1x, /_next/image?url=%2Ftest.jpg&w=828&q=75 2x',
},
{
fetchpriority: 'high',
imagesizes: '100vw',
imagesrcset:
'/_next/image?url=%2Fwide.png&w=640&q=75 640w, /_next/image?url=%2Fwide.png&w=750&q=75 750w, /_next/image?url=%2Fwide.png&w=828&q=75 828w, /_next/image?url=%2Fwide.png&w=1080&q=75 1080w, /_next/image?url=%2Fwide.png&w=1200&q=75 1200w, /_next/image?url=%2Fwide.png&w=1920&q=75 1920w, /_next/image?url=%2Fwide.png&w=2048&q=75 2048w, /_next/image?url=%2Fwide.png&w=3840&q=75 3840w',
},
])

expect(
entries.find(
(item) =>
item.imagesrcset ===
'/_next/image?url=%2Ftest.jpg&w=640&q=75 1x, /_next/image?url=%2Ftest.jpg&w=828&q=75 2x'
)
).toEqual({
fetchpriority: 'high',
imagesizes: '',
imagesrcset:
'/_next/image?url=%2Ftest.jpg&w=640&q=75 1x, /_next/image?url=%2Ftest.jpg&w=828&q=75 2x',
})

expect(
entries.find(
(item) =>
item.imagesrcset ===
'/_next/image?url=%2Fwide.png&w=640&q=75 640w, /_next/image?url=%2Fwide.png&w=750&q=75 750w, /_next/image?url=%2Fwide.png&w=828&q=75 828w, /_next/image?url=%2Fwide.png&w=1080&q=75 1080w, /_next/image?url=%2Fwide.png&w=1200&q=75 1200w, /_next/image?url=%2Fwide.png&w=1920&q=75 1920w, /_next/image?url=%2Fwide.png&w=2048&q=75 2048w, /_next/image?url=%2Fwide.png&w=3840&q=75 3840w'
)
).toEqual({
fetchpriority: 'high',
imagesizes: '100vw',
imagesrcset:
'/_next/image?url=%2Fwide.png&w=640&q=75 640w, /_next/image?url=%2Fwide.png&w=750&q=75 750w, /_next/image?url=%2Fwide.png&w=828&q=75 828w, /_next/image?url=%2Fwide.png&w=1080&q=75 1080w, /_next/image?url=%2Fwide.png&w=1200&q=75 1200w, /_next/image?url=%2Fwide.png&w=1920&q=75 1920w, /_next/image?url=%2Fwide.png&w=2048&q=75 2048w, /_next/image?url=%2Fwide.png&w=3840&q=75 3840w',
})

// When priority={true}, we should _not_ set loading="lazy"
expect(
Expand Down
49 changes: 31 additions & 18 deletions test/integration/next-image-new/default/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,20 +126,31 @@ function runTests(mode) {
const imagesizes = await link.getAttribute('imagesizes')
entries.push({ fetchpriority, imagesrcset, imagesizes })
}
expect(entries).toEqual([
{
fetchpriority: 'high',
imagesizes: '',
imagesrcset:
'/_next/image?url=%2Ftest.jpg&w=640&q=75 1x, /_next/image?url=%2Ftest.jpg&w=828&q=75 2x',
},
{
fetchpriority: 'high',
imagesizes: '100vw',
imagesrcset:
'/_next/image?url=%2Fwide.png&w=640&q=75 640w, /_next/image?url=%2Fwide.png&w=750&q=75 750w, /_next/image?url=%2Fwide.png&w=828&q=75 828w, /_next/image?url=%2Fwide.png&w=1080&q=75 1080w, /_next/image?url=%2Fwide.png&w=1200&q=75 1200w, /_next/image?url=%2Fwide.png&w=1920&q=75 1920w, /_next/image?url=%2Fwide.png&w=2048&q=75 2048w, /_next/image?url=%2Fwide.png&w=3840&q=75 3840w',
},
])
expect(
entries.find(
(item) =>
item.imagesrcset ===
'/_next/image?url=%2Ftest.jpg&w=640&q=75 1x, /_next/image?url=%2Ftest.jpg&w=828&q=75 2x'
)
).toEqual({
fetchpriority: 'high',
imagesizes: '',
imagesrcset:
'/_next/image?url=%2Ftest.jpg&w=640&q=75 1x, /_next/image?url=%2Ftest.jpg&w=828&q=75 2x',
})

expect(
entries.find(
(item) =>
item.imagesrcset ===
'/_next/image?url=%2Fwide.png&w=640&q=75 640w, /_next/image?url=%2Fwide.png&w=750&q=75 750w, /_next/image?url=%2Fwide.png&w=828&q=75 828w, /_next/image?url=%2Fwide.png&w=1080&q=75 1080w, /_next/image?url=%2Fwide.png&w=1200&q=75 1200w, /_next/image?url=%2Fwide.png&w=1920&q=75 1920w, /_next/image?url=%2Fwide.png&w=2048&q=75 2048w, /_next/image?url=%2Fwide.png&w=3840&q=75 3840w'
)
).toEqual({
fetchpriority: 'high',
imagesizes: '100vw',
imagesrcset:
'/_next/image?url=%2Fwide.png&w=640&q=75 640w, /_next/image?url=%2Fwide.png&w=750&q=75 750w, /_next/image?url=%2Fwide.png&w=828&q=75 828w, /_next/image?url=%2Fwide.png&w=1080&q=75 1080w, /_next/image?url=%2Fwide.png&w=1200&q=75 1200w, /_next/image?url=%2Fwide.png&w=1920&q=75 1920w, /_next/image?url=%2Fwide.png&w=2048&q=75 2048w, /_next/image?url=%2Fwide.png&w=3840&q=75 3840w',
})

// When priority={true}, we should _not_ set loading="lazy"
expect(
Expand Down Expand Up @@ -187,10 +198,12 @@ function runTests(mode) {

// should preload with crossorigin
expect(
await browser.elementsByCss(
'link[rel=preload][as=image][crossorigin=anonymous][imagesrcset*="test.jpg"]'
)
).toHaveLength(1)
(
await browser.elementsByCss(
'link[rel=preload][as=image][crossorigin=anonymous][imagesrcset*="test.jpg"]'
)
).length
).toBeGreaterThanOrEqual(1)
} finally {
if (browser) {
await browser.close()
Expand Down