Skip to content

Commit 74ac764

Browse files
committed
make cache omission/delay consistent
1 parent 2ad2fc0 commit 74ac764

File tree

1 file changed

+138
-152
lines changed

1 file changed

+138
-152
lines changed

packages/next/src/server/use-cache/use-cache-wrapper.ts

Lines changed: 138 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,110 +1248,14 @@ export function cache(
12481248
if (cachedEntry !== undefined) {
12491249
const existingEntry = await cachedEntry
12501250
if (workUnitStore !== undefined && existingEntry !== undefined) {
1251-
if (
1252-
existingEntry.revalidate === 0 ||
1253-
existingEntry.expire < DYNAMIC_EXPIRE
1254-
) {
1255-
switch (workUnitStore.type) {
1256-
case 'prerender':
1257-
// In a Dynamic I/O prerender, if the cache entry has
1258-
// revalidate: 0 or if the expire time is under 5 minutes,
1259-
// then we consider this cache entry dynamic as it's not worth
1260-
// generating static pages for such data. It's better to leave
1261-
// a dynamic hole that can be filled in during the resume with
1262-
// a potentially cached entry.
1263-
if (cacheSignal) {
1264-
cacheSignal.endRead()
1265-
}
1266-
return makeHangingPromise(
1267-
workUnitStore.renderSignal,
1268-
workStore.route,
1269-
'dynamic "use cache"'
1270-
)
1271-
case 'prerender-runtime': {
1272-
// In the final phase of a runtime prerender, we have to make
1273-
// sure that APIs that would hang during a static prerender
1274-
// are resolved with a delay, in the runtime stage.
1275-
if (workUnitStore.runtimeStagePromise) {
1276-
await workUnitStore.runtimeStagePromise
1277-
}
1278-
break
1279-
}
1280-
case 'request': {
1281-
if (process.env.NODE_ENV === 'development') {
1282-
// We delay the cache here so that it doesn't resolve in the static task --
1283-
// in a regular static prerender, it'd be a hanging promise, and we need to reflect that,
1284-
// so it has to resolve later.
1285-
// TODO(restart-on-cache-miss): This can fallthrough to the `RUNTIME_PREFETCH_DYNAMIC_STALE`
1286-
// check below, and try to delay again. This is not incorrect, but it's unnecessary.
1287-
// refactor this to avoid it.
1288-
const hang = await delayOrHangStartedCacheReadInDev(
1289-
RenderStage.Runtime,
1290-
workUnitStore,
1291-
cacheSignal,
1292-
workStore.route,
1293-
'dynamic "use cache"'
1294-
)
1295-
if (hang) {
1296-
return hang.hangingPromise
1297-
}
1298-
}
1299-
break
1300-
}
1301-
case 'prerender-ppr':
1302-
case 'prerender-legacy':
1303-
case 'cache':
1304-
case 'private-cache':
1305-
case 'unstable-cache':
1306-
break
1307-
default:
1308-
workUnitStore satisfies never
1309-
}
1310-
}
1311-
1312-
if (existingEntry.stale < RUNTIME_PREFETCH_DYNAMIC_STALE) {
1313-
switch (workUnitStore.type) {
1314-
case 'prerender-runtime':
1315-
// In a runtime prerender, if the cache entry will become
1316-
// stale in less then 30 seconds, we consider this cache entry
1317-
// dynamic as it's not worth prefetching. It's better to leave
1318-
// a dynamic hole that can be filled during the navigation.
1319-
if (cacheSignal) {
1320-
cacheSignal.endRead()
1321-
}
1322-
return makeHangingPromise(
1323-
workUnitStore.renderSignal,
1324-
workStore.route,
1325-
'dynamic "use cache"'
1326-
)
1327-
case 'request': {
1328-
if (process.env.NODE_ENV === 'development') {
1329-
// We delay the cache here so that it doesn't resolve in the runtime phase --
1330-
// in a regular runtime prerender, it'd be a hanging promise, and we need to reflect that,
1331-
// so it has to resolve later.
1332-
const hang = await delayOrHangStartedCacheReadInDev(
1333-
RenderStage.Dynamic,
1334-
workUnitStore,
1335-
cacheSignal,
1336-
workStore.route,
1337-
'dynamic "use cache"'
1338-
)
1339-
if (hang) {
1340-
return hang.hangingPromise
1341-
}
1342-
}
1343-
break
1344-
}
1345-
case 'prerender':
1346-
case 'prerender-ppr':
1347-
case 'prerender-legacy':
1348-
case 'cache':
1349-
case 'private-cache':
1350-
case 'unstable-cache':
1351-
break
1352-
default:
1353-
workUnitStore satisfies never
1354-
}
1251+
const hang = await delayOrHangStartedCacheReadIfTooDynamic(
1252+
workStore,
1253+
workUnitStore,
1254+
existingEntry,
1255+
cacheSignal
1256+
)
1257+
if (hang) {
1258+
return hang.hangingPromise
13551259
}
13561260
}
13571261

@@ -1479,54 +1383,15 @@ export function cache(
14791383
}
14801384

14811385
const currentTime = performance.timeOrigin + performance.now()
1482-
if (
1483-
workUnitStore !== undefined &&
1484-
entry !== undefined &&
1485-
(entry.revalidate === 0 || entry.expire < DYNAMIC_EXPIRE)
1486-
) {
1487-
switch (workUnitStore.type) {
1488-
case 'prerender':
1489-
// In a Dynamic I/O prerender, if the cache entry has revalidate:
1490-
// 0 or if the expire time is under 5 minutes, then we consider
1491-
// this cache entry dynamic as it's not worth generating static
1492-
// pages for such data. It's better to leave a dynamic hole that
1493-
// can be filled in during the resume with a potentially cached
1494-
// entry.
1495-
if (cacheSignal) {
1496-
cacheSignal.endRead()
1497-
}
1498-
return makeHangingPromise(
1499-
workUnitStore.renderSignal,
1500-
workStore.route,
1501-
'dynamic "use cache"'
1502-
)
1503-
case 'request': {
1504-
if (process.env.NODE_ENV === 'development') {
1505-
// We delay the cache here so that it doesn't resolve in the static task --
1506-
// in a regular static prerender, it'd be a hanging promise, and we need to reflect that,
1507-
// so it has to resolve later.
1508-
const hang = await delayOrHangStartedCacheReadInDev(
1509-
RenderStage.Dynamic,
1510-
workUnitStore,
1511-
cacheSignal,
1512-
workStore.route,
1513-
'dynamic "use cache"'
1514-
)
1515-
if (hang) {
1516-
return hang.hangingPromise
1517-
}
1518-
}
1519-
break
1520-
}
1521-
case 'prerender-runtime':
1522-
case 'prerender-ppr':
1523-
case 'prerender-legacy':
1524-
case 'cache':
1525-
case 'private-cache':
1526-
case 'unstable-cache':
1527-
break
1528-
default:
1529-
workUnitStore satisfies never
1386+
if (workUnitStore !== undefined && entry !== undefined) {
1387+
const hang = await delayOrHangStartedCacheReadIfTooDynamic(
1388+
workStore,
1389+
workUnitStore,
1390+
entry,
1391+
cacheSignal
1392+
)
1393+
if (hang) {
1394+
return hang.hangingPromise
15301395
}
15311396
}
15321397

@@ -1717,6 +1582,127 @@ export function cache(
17171582
return React.cache(cachedFn)
17181583
}
17191584

1585+
/**
1586+
* If we're in a prerender-like environment, caches that would expire
1587+
* or go stale too quickly should be omitted from the rendered result.
1588+
* In multi-stage renders, they should be delayed to the appropriate stage.
1589+
*/
1590+
async function delayOrHangStartedCacheReadIfTooDynamic(
1591+
workStore: WorkStore,
1592+
workUnitStore: WorkUnitStore,
1593+
entry: CacheEntry,
1594+
cacheSignal: CacheSignal | null
1595+
): Promise<{ hangingPromise: Promise<never> } | null> {
1596+
if (entry.revalidate === 0 || entry.expire < DYNAMIC_EXPIRE) {
1597+
switch (workUnitStore.type) {
1598+
case 'prerender':
1599+
// In a Dynamic I/O prerender, if the cache entry has
1600+
// revalidate: 0 or if the expire time is under 5 minutes,
1601+
// then we consider this cache entry dynamic as it's not worth
1602+
// generating static pages for such data. It's better to leave
1603+
// a dynamic hole that can be filled in during the resume with
1604+
// a potentially cached entry.
1605+
if (cacheSignal) {
1606+
cacheSignal.endRead()
1607+
}
1608+
const hangingPromise = makeHangingPromise<never>(
1609+
workUnitStore.renderSignal,
1610+
workStore.route,
1611+
'dynamic "use cache"'
1612+
)
1613+
return { hangingPromise }
1614+
case 'prerender-runtime': {
1615+
// In the final phase of a runtime prerender, we have to make
1616+
// sure that APIs that would hang during a static prerender
1617+
// are resolved with a delay, in the runtime stage.
1618+
if (workUnitStore.runtimeStagePromise) {
1619+
await workUnitStore.runtimeStagePromise
1620+
}
1621+
break
1622+
}
1623+
case 'request': {
1624+
if (process.env.NODE_ENV === 'development') {
1625+
// We delay the cache here so that it doesn't resolve in the static task --
1626+
// in a regular static prerender, it'd be a hanging promise, and we need to reflect that,
1627+
// so it has to resolve later.
1628+
// TODO(restart-on-cache-miss): This can fallthrough to the `RUNTIME_PREFETCH_DYNAMIC_STALE`
1629+
// check below, and try to delay again. This is not incorrect, but it's unnecessary.
1630+
// refactor this to avoid it.
1631+
const hang = await delayOrHangStartedCacheReadInDev(
1632+
RenderStage.Runtime,
1633+
workUnitStore,
1634+
cacheSignal,
1635+
workStore.route,
1636+
'dynamic "use cache"'
1637+
)
1638+
if (hang) {
1639+
return hang
1640+
}
1641+
}
1642+
break
1643+
}
1644+
case 'prerender-client':
1645+
case 'prerender-ppr':
1646+
case 'prerender-legacy':
1647+
case 'cache':
1648+
case 'private-cache':
1649+
case 'unstable-cache':
1650+
break
1651+
default:
1652+
workUnitStore satisfies never
1653+
}
1654+
}
1655+
1656+
if (entry.stale < RUNTIME_PREFETCH_DYNAMIC_STALE) {
1657+
switch (workUnitStore.type) {
1658+
case 'prerender-runtime':
1659+
// In a runtime prerender, if the cache entry will become
1660+
// stale in less then 30 seconds, we consider this cache entry
1661+
// dynamic as it's not worth prefetching. It's better to leave
1662+
// a dynamic hole that can be filled during the navigation.
1663+
if (cacheSignal) {
1664+
cacheSignal.endRead()
1665+
}
1666+
const hangingPromise = makeHangingPromise<never>(
1667+
workUnitStore.renderSignal,
1668+
workStore.route,
1669+
'dynamic "use cache"'
1670+
)
1671+
return { hangingPromise }
1672+
case 'request': {
1673+
if (process.env.NODE_ENV === 'development') {
1674+
// We delay the cache here so that it doesn't resolve in the runtime phase --
1675+
// in a regular runtime prerender, it'd be a hanging promise, and we need to reflect that,
1676+
// so it has to resolve later.
1677+
const hang = await delayOrHangStartedCacheReadInDev(
1678+
RenderStage.Dynamic,
1679+
workUnitStore,
1680+
cacheSignal,
1681+
workStore.route,
1682+
'dynamic "use cache"'
1683+
)
1684+
if (hang) {
1685+
return hang
1686+
}
1687+
}
1688+
break
1689+
}
1690+
case 'prerender':
1691+
case 'prerender-client':
1692+
case 'prerender-ppr':
1693+
case 'prerender-legacy':
1694+
case 'cache':
1695+
case 'private-cache':
1696+
case 'unstable-cache':
1697+
break
1698+
default:
1699+
workUnitStore satisfies never
1700+
}
1701+
}
1702+
1703+
return null
1704+
}
1705+
17201706
/**
17211707
* Returns `true` if the `'use cache'` function is the page component itself,
17221708
* or `generateMetadata`/`generateViewport` in a page file.

0 commit comments

Comments
 (0)