1
+ import { DetachedPromise } from "utils/promise.js" ;
2
+
1
3
import { IncrementalCache } from "../cache/incremental/types.js" ;
2
4
import { TagCache } from "../cache/tag/types.js" ;
3
5
import { isBinaryContentType } from "./binary.js" ;
@@ -144,7 +146,8 @@ export default class S3Cache {
144
146
value : value ,
145
147
} as CacheHandlerValue ;
146
148
} catch ( e ) {
147
- error ( "Failed to get fetch cache" , e ) ;
149
+ // We can usually ignore errors here as they are usually due to cache not being found
150
+ debug ( "Failed to get fetch cache" , e ) ;
148
151
return null ;
149
152
}
150
153
}
@@ -166,7 +169,7 @@ export default class S3Cache {
166
169
// If some tags are stale we need to force revalidation
167
170
return null ;
168
171
}
169
- const requestId = globalThis . __als . getStore ( ) ?? "" ;
172
+ const requestId = globalThis . __als . getStore ( ) ?. requestId ?? "" ;
170
173
globalThis . lastModified [ requestId ] = _lastModified ;
171
174
if ( cacheData ?. type === "route" ) {
172
175
return {
@@ -208,7 +211,8 @@ export default class S3Cache {
208
211
return null ;
209
212
}
210
213
} catch ( e ) {
211
- error ( "Failed to get body cache" , e ) ;
214
+ // We can usually ignore errors here as they are usually due to cache not being found
215
+ debug ( "Failed to get body cache" , e ) ;
212
216
return null ;
213
217
}
214
218
}
@@ -221,99 +225,115 @@ export default class S3Cache {
221
225
if ( globalThis . disableIncrementalCache ) {
222
226
return ;
223
227
}
224
- if ( data ?. kind === "ROUTE" ) {
225
- const { body, status, headers } = data ;
226
- await globalThis . incrementalCache . set (
227
- key ,
228
- {
229
- type : "route" ,
230
- body : body . toString (
231
- isBinaryContentType ( String ( headers [ "content-type" ] ) )
232
- ? "base64"
233
- : "utf8" ,
234
- ) ,
235
- meta : {
236
- status,
237
- headers,
238
- } ,
239
- } ,
240
- false ,
241
- ) ;
242
- } else if ( data ?. kind === "PAGE" ) {
243
- const { html, pageData } = data ;
244
- const isAppPath = typeof pageData === "string" ;
245
- if ( isAppPath ) {
246
- globalThis . incrementalCache . set (
228
+ const detachedPromise = new DetachedPromise < void > ( ) ;
229
+ globalThis . __als . getStore ( ) ?. pendingPromises . push ( detachedPromise ) ;
230
+ try {
231
+ if ( data ?. kind === "ROUTE" ) {
232
+ const { body, status, headers } = data ;
233
+ await globalThis . incrementalCache . set (
247
234
key ,
248
235
{
249
- type : "app" ,
250
- html,
251
- rsc : pageData ,
236
+ type : "route" ,
237
+ body : body . toString (
238
+ isBinaryContentType ( String ( headers [ "content-type" ] ) )
239
+ ? "base64"
240
+ : "utf8" ,
241
+ ) ,
242
+ meta : {
243
+ status,
244
+ headers,
245
+ } ,
252
246
} ,
253
247
false ,
254
248
) ;
255
- } else {
256
- globalThis . incrementalCache . set (
249
+ } else if ( data ?. kind === "PAGE" ) {
250
+ const { html, pageData } = data ;
251
+ const isAppPath = typeof pageData === "string" ;
252
+ if ( isAppPath ) {
253
+ globalThis . incrementalCache . set (
254
+ key ,
255
+ {
256
+ type : "app" ,
257
+ html,
258
+ rsc : pageData ,
259
+ } ,
260
+ false ,
261
+ ) ;
262
+ } else {
263
+ globalThis . incrementalCache . set (
264
+ key ,
265
+ {
266
+ type : "page" ,
267
+ html,
268
+ json : pageData ,
269
+ } ,
270
+ false ,
271
+ ) ;
272
+ }
273
+ } else if ( data ?. kind === "FETCH" ) {
274
+ await globalThis . incrementalCache . set < true > ( key , data , true ) ;
275
+ } else if ( data ?. kind === "REDIRECT" ) {
276
+ await globalThis . incrementalCache . set (
257
277
key ,
258
278
{
259
- type : "page" ,
260
- html,
261
- json : pageData ,
279
+ type : "redirect" ,
280
+ props : data . props ,
262
281
} ,
263
282
false ,
264
283
) ;
284
+ } else if ( data === null || data === undefined ) {
285
+ await globalThis . incrementalCache . delete ( key ) ;
265
286
}
266
- } else if ( data ?. kind === "FETCH" ) {
267
- await globalThis . incrementalCache . set < true > ( key , data , true ) ;
268
- } else if ( data ?. kind === "REDIRECT" ) {
269
- await globalThis . incrementalCache . set (
270
- key ,
271
- {
272
- type : "redirect" ,
273
- props : data . props ,
274
- } ,
275
- false ,
276
- ) ;
277
- } else if ( data === null || data === undefined ) {
278
- await globalThis . incrementalCache . delete ( key ) ;
279
- }
280
- // Write derivedTags to dynamodb
281
- // If we use an in house version of getDerivedTags in build we should use it here instead of next's one
282
- const derivedTags : string [ ] =
283
- data ?. kind === "FETCH"
284
- ? ctx ?. tags ?? data ?. data ?. tags ?? [ ] // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
285
- : data ?. kind === "PAGE"
286
- ? data . headers ?. [ "x-next-cache-tags" ] ?. split ( "," ) ?? [ ]
287
- : [ ] ;
288
- debug ( "derivedTags" , derivedTags ) ;
289
- // Get all tags stored in dynamodb for the given key
290
- // If any of the derived tags are not stored in dynamodb for the given key, write them
291
- const storedTags = await globalThis . tagCache . getByPath ( key ) ;
292
- const tagsToWrite = derivedTags . filter ( ( tag ) => ! storedTags . includes ( tag ) ) ;
293
- if ( tagsToWrite . length > 0 ) {
294
- await globalThis . tagCache . writeTags (
295
- tagsToWrite . map ( ( tag ) => ( {
296
- path : key ,
297
- tag : tag ,
298
- } ) ) ,
287
+ // Write derivedTags to dynamodb
288
+ // If we use an in house version of getDerivedTags in build we should use it here instead of next's one
289
+ const derivedTags : string [ ] =
290
+ data ?. kind === "FETCH"
291
+ ? ctx ?. tags ?? data ?. data ?. tags ?? [ ] // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
292
+ : data ?. kind === "PAGE"
293
+ ? data . headers ?. [ "x-next-cache-tags" ] ?. split ( "," ) ?? [ ]
294
+ : [ ] ;
295
+ debug ( "derivedTags" , derivedTags ) ;
296
+ // Get all tags stored in dynamodb for the given key
297
+ // If any of the derived tags are not stored in dynamodb for the given key, write them
298
+ const storedTags = await globalThis . tagCache . getByPath ( key ) ;
299
+ const tagsToWrite = derivedTags . filter (
300
+ ( tag ) => ! storedTags . includes ( tag ) ,
299
301
) ;
302
+ if ( tagsToWrite . length > 0 ) {
303
+ await globalThis . tagCache . writeTags (
304
+ tagsToWrite . map ( ( tag ) => ( {
305
+ path : key ,
306
+ tag : tag ,
307
+ } ) ) ,
308
+ ) ;
309
+ }
310
+ debug ( "Finished setting cache" ) ;
311
+ } catch ( e ) {
312
+ error ( "Failed to set cache" , e ) ;
313
+ } finally {
314
+ // We need to resolve the promise even if there was an error
315
+ detachedPromise . resolve ( ) ;
300
316
}
301
317
}
302
318
303
319
public async revalidateTag ( tag : string ) {
304
320
if ( globalThis . disableDynamoDBCache || globalThis . disableIncrementalCache ) {
305
321
return ;
306
322
}
307
- debug ( "revalidateTag" , tag ) ;
308
- // Find all keys with the given tag
309
- const paths = await globalThis . tagCache . getByTag ( tag ) ;
310
- debug ( "Items" , paths ) ;
311
- // Update all keys with the given tag with revalidatedAt set to now
312
- await globalThis . tagCache . writeTags (
313
- paths ?. map ( ( path ) => ( {
314
- path : path ,
315
- tag : tag ,
316
- } ) ) ?? [ ] ,
317
- ) ;
323
+ try {
324
+ debug ( "revalidateTag" , tag ) ;
325
+ // Find all keys with the given tag
326
+ const paths = await globalThis . tagCache . getByTag ( tag ) ;
327
+ debug ( "Items" , paths ) ;
328
+ // Update all keys with the given tag with revalidatedAt set to now
329
+ await globalThis . tagCache . writeTags (
330
+ paths ?. map ( ( path ) => ( {
331
+ path : path ,
332
+ tag : tag ,
333
+ } ) ) ?? [ ] ,
334
+ ) ;
335
+ } catch ( e ) {
336
+ error ( "Failed to revalidate tag" , e ) ;
337
+ }
318
338
}
319
339
}
0 commit comments