@@ -155,7 +155,7 @@ export class RedisStore implements Store {
155155 */
156156 async retryableIncrement ( _key : string ) : Promise < RedisReply > {
157157 const key = this . prefixKey ( _key )
158- const evalCommand = async ( ) =>
158+ const evalShaCommand = async ( ) =>
159159 this . sendCommand ( {
160160 key,
161161 isReadOnly : false ,
@@ -169,12 +169,28 @@ export class RedisStore implements Store {
169169 ] ,
170170 } )
171171
172+ const evalCommand = async ( ) =>
173+ this . sendCommand ( {
174+ key,
175+ isReadOnly : false ,
176+ command : [
177+ 'EVAL' ,
178+ scripts . increment ,
179+ '1' ,
180+ key ,
181+ this . resetExpiryOnChange ? '1' : '0' ,
182+ this . windowMs . toString ( ) ,
183+ ] ,
184+ } )
185+
172186 try {
173- const result = await evalCommand ( )
187+ const result = await evalShaCommand ( )
174188 return result
175189 } catch {
176- // TODO: distinguish different error types
177- this . incrementScriptSha = this . loadIncrementScript ( key )
190+ // In Redis Cluster, scripts loaded on one node may not be available
191+ // on the node where the key is located. Fall back to EVAL.
192+ // We don't need to check for NOSCRIPT explicitly because if EVALSHA fails
193+ // for ANY reason, EVAL is a safe fallback that guarantees execution.
178194 return evalCommand ( )
179195 }
180196 }
@@ -209,17 +225,22 @@ export class RedisStore implements Store {
209225 async get ( _key : string ) : Promise < ClientRateLimitInfo | undefined > {
210226 const key = this . prefixKey ( _key )
211227 let results
212- const evalCommand = async ( ) =>
228+ const evalShaCommand = async ( ) =>
213229 this . sendCommand ( {
214230 key,
215231 isReadOnly : true ,
216232 command : [ 'EVALSHA' , await this . getScriptSha , '1' , key ] ,
217233 } )
234+ const evalCommand = async ( ) =>
235+ this . sendCommand ( {
236+ key,
237+ isReadOnly : true ,
238+ command : [ 'EVAL' , scripts . get , '1' , key ] ,
239+ } )
218240 try {
219- results = await evalCommand ( )
241+ results = await evalShaCommand ( )
220242 } catch {
221- // TODO: distinguish different error types
222- this . getScriptSha = this . loadGetScript ( key )
243+ // Fallback to EVAL if EVALSHA fails
223244 results = await evalCommand ( )
224245 }
225246
0 commit comments