1
1
import { getDeployStore , GetWithMetadataOptions , Store } from '@netlify/blobs'
2
2
3
+ import { getRequestContext , RequestContext } from './handlers/request-context.cjs'
3
4
import { getTracer } from './handlers/tracer.cjs'
4
5
5
6
const FETCH_BEFORE_NEXT_PATCHED_IT = Symbol . for ( 'nf-not-patched-fetch' )
@@ -68,6 +69,44 @@ const encodeBlobKey = async (key: string) => {
68
69
return await encodeBlobKeyImpl ( key )
69
70
}
70
71
72
+ // hold in-memory cache tied to request in memory for as long as request context is alive
73
+ const perRequestInMemoryStore = new WeakMap <
74
+ RequestContext ,
75
+ Record < string , Promise < unknown > | unknown >
76
+ > ( )
77
+
78
+ const getRequestSpecificInMemoryCache = ( ) => {
79
+ const requestContext = getRequestContext ( )
80
+ if ( ! requestContext ) {
81
+ // Fallback to a no-op store if we can't find request context
82
+ return {
83
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
+ get < T > ( key : string ) : Promise < T | null > | undefined {
85
+ return undefined
86
+ } ,
87
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
88
+ set ( key : string , value : unknown ) {
89
+ // no-op
90
+ } ,
91
+ }
92
+ }
93
+
94
+ let perRequestStore = perRequestInMemoryStore . get ( requestContext )
95
+ if ( ! perRequestStore ) {
96
+ perRequestStore = { }
97
+ perRequestInMemoryStore . set ( requestContext , perRequestStore )
98
+ }
99
+
100
+ return {
101
+ get < T > ( key : string ) : Promise < T | null > | undefined {
102
+ return perRequestStore [ key ] as Promise < T | null > | undefined
103
+ } ,
104
+ set ( key : string , value : unknown ) {
105
+ perRequestStore [ key ] = value
106
+ } ,
107
+ }
108
+ }
109
+
71
110
export const getMemoizedKeyValueStoreBackedByRegionalBlobStore = (
72
111
args : GetWithMetadataOptions = { } ,
73
112
) => {
@@ -76,21 +115,32 @@ export const getMemoizedKeyValueStoreBackedByRegionalBlobStore = (
76
115
77
116
return {
78
117
async get < T > ( key : string , otelSpanTitle : string ) : Promise < T | null > {
79
- const blobKey = await encodeBlobKey ( key )
118
+ const inMemoryCache = getRequestSpecificInMemoryCache ( )
80
119
81
- return tracer . withActiveSpan ( otelSpanTitle , async ( span ) => {
120
+ const memoizedValue = inMemoryCache . get < T > ( key )
121
+ if ( memoizedValue ) {
122
+ return memoizedValue
123
+ }
124
+
125
+ const blobKey = await encodeBlobKey ( key )
126
+ const getPromise = tracer . withActiveSpan ( otelSpanTitle , async ( span ) => {
82
127
span . setAttributes ( { key, blobKey } )
83
- const blob = await store . get ( key , { type : 'json' } )
128
+ const blob = await store . get ( blobKey , { type : 'json' } )
84
129
span . addEvent ( blob ? 'Hit' : 'Miss' )
85
130
return blob
86
131
} )
132
+ inMemoryCache . set ( key , getPromise )
133
+ return getPromise
87
134
} ,
88
135
async set ( key : string , value : unknown , otelSpanTitle : string ) {
89
- const blobKey = await encodeBlobKey ( key )
136
+ const inMemoryCache = getRequestSpecificInMemoryCache ( )
90
137
138
+ inMemoryCache . set ( key , value )
139
+
140
+ const blobKey = await encodeBlobKey ( key )
91
141
return tracer . withActiveSpan ( otelSpanTitle , async ( span ) => {
92
142
span . setAttributes ( { key, blobKey } )
93
- return await store . setJSON ( key , value )
143
+ return await store . setJSON ( blobKey , value )
94
144
} )
95
145
} ,
96
146
}
0 commit comments