@@ -1508,43 +1508,65 @@ export function cache(
15081508 // If this is stale, and we're not in a prerender (i.e. this is
15091509 // dynamic render), then we should warm up the cache with a fresh
15101510 // revalidated entry.
1511- const result = await generateCacheEntry (
1512- workStore ,
1513- // This is not running within the context of this unit.
1514- { kind : cacheContext . kind , outerWorkUnitStore : undefined } ,
1515- clientReferenceManifest ,
1516- encodedCacheKeyParts ,
1517- fn ,
1518- sharedErrorStack
1519- )
1511+ const revalidateAndWriteNewEntry = async ( ) => {
1512+ const result = await generateCacheEntry (
1513+ workStore ,
1514+ // This is not running within the context of this unit.
1515+ { kind : cacheContext . kind , outerWorkUnitStore : undefined } ,
1516+ clientReferenceManifest ,
1517+ encodedCacheKeyParts ,
1518+ fn ,
1519+ sharedErrorStack
1520+ )
15201521
1521- if ( result . type === 'cached' ) {
1522- const { stream : ignoredStream , pendingCacheEntry } = result
1523- let savedCacheEntry : Promise < CacheEntry >
1524-
1525- if ( prerenderResumeDataCache ) {
1526- const split = clonePendingCacheEntry ( pendingCacheEntry )
1527- savedCacheEntry = getNthCacheEntry ( split , 0 )
1528- prerenderResumeDataCache . cache . set (
1529- serializedCacheKey ,
1530- getNthCacheEntry ( split , 1 )
1531- )
1532- } else {
1533- savedCacheEntry = pendingCacheEntry
1534- }
1522+ if ( result . type === 'cached' ) {
1523+ const { stream : ignoredStream , pendingCacheEntry } = result
1524+ let savedCacheEntry : Promise < CacheEntry >
1525+
1526+ // TODO: do we ever want to mutate the `prerenderResumeDataCache` while a render is ongoing?
1527+ if (
1528+ prerenderResumeDataCache &&
1529+ // This is not covered by a cache read, so in a dev staged render
1530+ // we don't want to overwrite the existing cache entry
1531+ // both to avoid timing issues (with cache entries ehtat aren't finished yet) and tearing.
1532+ ! (
1533+ process . env . NODE_ENV === 'development' &&
1534+ workUnitStore &&
1535+ // eslint-disable-next-line no-restricted-syntax
1536+ workUnitStore . type === 'request' &&
1537+ workUnitStore . stagedRendering
1538+ )
1539+ ) {
1540+ const split = clonePendingCacheEntry ( pendingCacheEntry )
1541+ savedCacheEntry = getNthCacheEntry ( split , 0 )
1542+ prerenderResumeDataCache . cache . set (
1543+ serializedCacheKey ,
1544+ getNthCacheEntry ( split , 1 )
1545+ )
1546+ } else {
1547+ savedCacheEntry = pendingCacheEntry
1548+ }
15351549
1536- if ( cacheHandler ) {
1537- const promise = cacheHandler . set (
1538- serializedCacheKey ,
1539- savedCacheEntry
1540- )
1550+ let pendingWrite : Promise < void > | undefined
1551+ if ( cacheHandler ) {
1552+ pendingWrite = cacheHandler . set (
1553+ serializedCacheKey ,
1554+ savedCacheEntry
1555+ )
1556+ }
15411557
1542- workStore . pendingRevalidateWrites ??= [ ]
1543- workStore . pendingRevalidateWrites . push ( promise )
1558+ await ignoredStream . cancel ( )
1559+ return pendingWrite
15441560 }
1545-
1546- await ignoredStream . cancel ( )
15471561 }
1562+
1563+ // TODO: ideally, this'd be scheduled to run after the render is finished
1564+ // to avoid interfering with any critical work.
1565+ const pendingRevalidatePromise = new Promise ( ( resolve ) =>
1566+ setTimeout ( resolve )
1567+ ) . then ( ( ) => revalidateAndWriteNewEntry ( ) )
1568+ workStore . pendingRevalidateWrites ??= [ ]
1569+ workStore . pendingRevalidateWrites . push ( pendingRevalidatePromise )
15481570 }
15491571 }
15501572 }
0 commit comments