Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit530d2c5

Browse files
authored
fix: handle case of zero-length cacheable route handler responses (#2819)
* test: add test case for route with 0-length response* test: add new blob key to test checking prerendered blobs after changing fixture* fix: ensure size calculation returns positive numbers and make in-memory cache failures not fatal* fix: drop unneded extra variable and also capture calculated size in warning
1 parent8a63ac2 commit530d2c5

File tree

3 files changed

+80
-24
lines changed

3 files changed

+80
-24
lines changed

‎src/run/storage/request-scoped-in-memory-cache.cts

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,44 +25,70 @@ export function setInMemoryCacheMaxSizeFromNextConfig(size: unknown) {
2525
}
2626
}
2727

28-
constestimateBlobSize=(valueToStore:BlobType|null|Promise<unknown>):number=>{
28+
typePositiveNumber=number&{__positive:true}
29+
constisPositiveNumber=(value:unknown):value isPositiveNumber=>{
30+
returntypeofvalue==='number'&&value>0
31+
}
32+
33+
constBASE_BLOB_SIZE=25asPositiveNumber
34+
35+
constestimateBlobKnownTypeSize=(
36+
valueToStore:BlobType|null|Promise<unknown>,
37+
):number|undefined=>{
2938
// very approximate size calculation to avoid expensive exact size calculation
3039
// inspired by https://github.com/vercel/next.js/blob/ed10f7ed0246fcc763194197eb9beebcbd063162/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L60-L79
3140
if(valueToStore===null||isPromise(valueToStore)||isTagManifest(valueToStore)){
32-
return25
41+
returnBASE_BLOB_SIZE
3342
}
3443
if(isHtmlBlob(valueToStore)){
35-
returnvalueToStore.html.length
44+
returnBASE_BLOB_SIZE+valueToStore.html.length
45+
}
46+
47+
if(valueToStore.value?.kind==='FETCH'){
48+
returnBASE_BLOB_SIZE+valueToStore.value.data.body.length
49+
}
50+
if(valueToStore.value?.kind==='APP_PAGE'){
51+
return(
52+
BASE_BLOB_SIZE+valueToStore.value.html.length+(valueToStore.value.rscData?.length??0)
53+
)
54+
}
55+
if(valueToStore.value?.kind==='PAGE'||valueToStore.value?.kind==='PAGES'){
56+
return(
57+
BASE_BLOB_SIZE+
58+
valueToStore.value.html.length+
59+
JSON.stringify(valueToStore.value.pageData).length
60+
)
3661
}
37-
letknownKindFailed=false
62+
if(valueToStore.value?.kind==='ROUTE'||valueToStore.value?.kind==='APP_ROUTE'){
63+
returnBASE_BLOB_SIZE+valueToStore.value.body.length
64+
}
65+
}
66+
67+
constestimateBlobSize=(valueToStore:BlobType|null|Promise<unknown>):PositiveNumber=>{
68+
letestimatedKnownTypeSize:number|undefined
69+
letestimateBlobKnownTypeSizeError:unknown
3870
try{
39-
if(valueToStore.value?.kind==='FETCH'){
40-
returnvalueToStore.value.data.body.length
41-
}
42-
if(valueToStore.value?.kind==='APP_PAGE'){
43-
returnvalueToStore.value.html.length+(valueToStore.value.rscData?.length??0)
71+
estimatedKnownTypeSize=estimateBlobKnownTypeSize(valueToStore)
72+
if(isPositiveNumber(estimatedKnownTypeSize)){
73+
returnestimatedKnownTypeSize
4474
}
45-
if(valueToStore.value?.kind==='PAGE'||valueToStore.value?.kind==='PAGES'){
46-
returnvalueToStore.value.html.length+JSON.stringify(valueToStore.value.pageData).length
47-
}
48-
if(valueToStore.value?.kind==='ROUTE'||valueToStore.value?.kind==='APP_ROUTE'){
49-
returnvalueToStore.value.body.length
50-
}
51-
}catch{
52-
// size calculation rely on the shape of the value, so if it's not what we expect, we fallback to JSON.stringify
53-
knownKindFailed=true
75+
}catch(error){
76+
estimateBlobKnownTypeSizeError=error
5477
}
5578

56-
// fallback for not known kinds or known kinds that did fail to calculate size
79+
// fallback for not known kinds or known kinds that did fail to calculate positive size
80+
constcalculatedSize=JSON.stringify(valueToStore).length
81+
5782
// we should also monitor cases when fallback is used because it's not the most efficient way to calculate/estimate size
5883
// and might indicate need to make adjustments or additions to the size calculation
5984
recordWarning(
6085
newError(
61-
`Blob size calculation did fallback to JSON.stringify. Kind: KnownKindFailed:${knownKindFailed},${valueToStore.value?.kind??'undefined'}`,
86+
`Blob size calculation did fallback to JSON.stringify. EstimatedKnownTypeSize:${estimatedKnownTypeSize}, CalculatedSize:${calculatedSize}, ValueToStore:${JSON.stringify(valueToStore)}`,
87+
estimateBlobKnownTypeSizeError ?{cause:estimateBlobKnownTypeSizeError} :undefined,
6288
),
6389
)
6490

65-
returnJSON.stringify(valueToStore).length
91+
returnisPositiveNumber(calculatedSize) ?calculatedSize :BASE_BLOB_SIZE
6692
}
6793

6894
functiongetInMemoryLRUCache(){
@@ -98,12 +124,26 @@ export const getRequestScopedInMemoryCache = (): RequestScopedInMemoryCache => {
98124
return{
99125
get(key){
100126
if(!requestContext)return
101-
constvalue=inMemoryLRUCache?.get(`${requestContext.requestID}:${key}`)
102-
returnvalue===NullValue ?null :value
127+
try{
128+
constvalue=inMemoryLRUCache?.get(`${requestContext.requestID}:${key}`)
129+
returnvalue===NullValue ?null :value
130+
}catch(error){
131+
// using in-memory store is perf optimization not requirement
132+
// trying to use optimization should NOT cause crashes
133+
// so we just record warning and return undefined
134+
recordWarning(newError('Failed to get value from memory cache',{cause:error}))
135+
}
103136
},
104137
set(key,value){
105138
if(!requestContext)return
106-
inMemoryLRUCache?.set(`${requestContext?.requestID}:${key}`,value??NullValue)
139+
try{
140+
inMemoryLRUCache?.set(`${requestContext?.requestID}:${key}`,value??NullValue)
141+
}catch(error){
142+
// using in-memory store is perf optimization not requirement
143+
// trying to use optimization should NOT cause crashes
144+
// so we just record warning and return undefined
145+
recordWarning(newError('Failed to store value in memory cache',{cause:error}))
146+
}
107147
},
108148
}
109149
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
exportasyncfunctionGET(){
2+
returnnewResponse('')
3+
}
4+
5+
exportconstdynamic='force-static'

‎tests/integration/cache-handler.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ describe('plugin', () => {
367367
'/api/revalidate-handler',
368368
'/api/static/first',
369369
'/api/static/second',
370+
'/api/zero-length-response',
370371
'/index',
371372
'/product/事前レンダリング,test',
372373
'/revalidate-fetch',
@@ -508,4 +509,14 @@ describe('route', () => {
508509

509510
expect(call2.body).toBe('{"params":{"slug":"not-in-generateStaticParams"}}')
510511
})
512+
513+
test<FixtureTestContext>('cacheable route handler response with 0 length response is served correctly',async(ctx)=>{
514+
awaitcreateFixture('server-components',ctx)
515+
awaitrunPlugin(ctx)
516+
517+
constcall=awaitinvokeFunction(ctx,{url:'/api/zero-length-response'})
518+
519+
expect(call.statusCode).toBe(200)
520+
expect(call.body).toBe('')
521+
})
511522
})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp