@@ -25,44 +25,71 @@ export function setInMemoryCacheMaxSizeFromNextConfig(size: unknown) {
2525 }
2626}
2727
28- const estimateBlobSize = ( valueToStore : BlobType | null | Promise < unknown > ) : number => {
28+ type PositiveNumber = number & { __positive : true }
29+ const isPositiveNumber = ( value : unknown ) : value is PositiveNumber => {
30+ return typeof value === 'number' && value > 0
31+ }
32+
33+ const BASE_BLOB_SIZE = 25 as PositiveNumber
34+
35+ const estimateBlobKnownTypeSize = (
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- return 25
41+ return BASE_BLOB_SIZE
3342 }
3443 if ( isHtmlBlob ( valueToStore ) ) {
35- return valueToStore . html . length
44+ return BASE_BLOB_SIZE + valueToStore . html . length
45+ }
46+
47+ if ( valueToStore . value ?. kind === 'FETCH' ) {
48+ return BASE_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- let knownKindFailed = false
62+ if ( valueToStore . value ?. kind === 'ROUTE' || valueToStore . value ?. kind === 'APP_ROUTE' ) {
63+ return BASE_BLOB_SIZE + valueToStore . value . body . length
64+ }
65+ }
66+
67+ const estimateBlobSize = ( valueToStore : BlobType | null | Promise < unknown > ) : PositiveNumber => {
68+ let knownTypeFailed = false
69+ let estimatedKnownTypeSize : number | undefined
70+ let estimateBlobKnownTypeSizeError : unknown
3871 try {
39- if ( valueToStore . value ?. kind === 'FETCH' ) {
40- return valueToStore . value . data . body . length
41- }
42- if ( valueToStore . value ?. kind === 'APP_PAGE' ) {
43- return valueToStore . value . html . length + ( valueToStore . value . rscData ?. length ?? 0 )
44- }
45- if ( valueToStore . value ?. kind === 'PAGE' || valueToStore . value ?. kind === 'PAGES' ) {
46- return valueToStore . value . html . length + JSON . stringify ( valueToStore . value . pageData ) . length
47- }
48- if ( valueToStore . value ?. kind === 'ROUTE' || valueToStore . value ?. kind === 'APP_ROUTE' ) {
49- return valueToStore . value . body . length
72+ estimatedKnownTypeSize = estimateBlobKnownTypeSize ( valueToStore )
73+ if ( isPositiveNumber ( estimatedKnownTypeSize ) ) {
74+ return estimatedKnownTypeSize
5075 }
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
76+ } catch ( error ) {
77+ knownTypeFailed = true
78+ estimateBlobKnownTypeSizeError = error
5479 }
5580
56- // fallback for not known kinds or known kinds that did fail to calculate size
81+ // fallback for not known kinds or known kinds that did fail to calculate positive size
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 new Error (
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. KnownTypeFailed: ${ knownTypeFailed } , EstimatedKnownTypeSize: ${ estimatedKnownTypeSize } , ValueToStore: ${ JSON . stringify ( valueToStore ) } ` ,
87+ estimateBlobKnownTypeSizeError ? { cause : estimateBlobKnownTypeSizeError } : undefined ,
6288 ) ,
6389 )
6490
65- return JSON . stringify ( valueToStore ) . length
91+ const calculatedSize = JSON . stringify ( valueToStore ) . length
92+ return isPositiveNumber ( calculatedSize ) ? calculatedSize : BASE_BLOB_SIZE
6693}
6794
6895function getInMemoryLRUCache ( ) {
@@ -98,12 +125,26 @@ export const getRequestScopedInMemoryCache = (): RequestScopedInMemoryCache => {
98125 return {
99126 get ( key ) {
100127 if ( ! requestContext ) return
101- const value = inMemoryLRUCache ?. get ( `${ requestContext . requestID } :${ key } ` )
102- return value === NullValue ? null : value
128+ try {
129+ const value = inMemoryLRUCache ?. get ( `${ requestContext . requestID } :${ key } ` )
130+ return value === NullValue ? null : value
131+ } catch ( error ) {
132+ // using in-memory store is perf optimization not requirement
133+ // trying to use optimization should NOT cause crashes
134+ // so we just record warning and return undefined
135+ recordWarning ( new Error ( 'Failed to get value from memory cache' , { cause : error } ) )
136+ }
103137 } ,
104138 set ( key , value ) {
105139 if ( ! requestContext ) return
106- inMemoryLRUCache ?. set ( `${ requestContext ?. requestID } :${ key } ` , value ?? NullValue )
140+ try {
141+ inMemoryLRUCache ?. set ( `${ requestContext ?. requestID } :${ key } ` , value ?? NullValue )
142+ } catch ( error ) {
143+ // using in-memory store is perf optimization not requirement
144+ // trying to use optimization should NOT cause crashes
145+ // so we just record warning and return undefined
146+ recordWarning ( new Error ( 'Failed to store value in memory cache' , { cause : error } ) )
147+ }
107148 } ,
108149 }
109150}
0 commit comments