Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ratelimiter not working with aws elasticache redis 7.1 #3379

Closed
sastie-rai opened this issue Apr 30, 2024 · 10 comments
Closed

Ratelimiter not working with aws elasticache redis 7.1 #3379

sastie-rai opened this issue Apr 30, 2024 · 10 comments

Comments

@sastie-rai
Copy link

Describe the bug
When trying to use the rate limiter with elasticache serverless redis (7.1) the following error occurs:

2024-04-29T21:10:38.653-05:00 DEBUG [api-gateway,,] 5553 --- [api-gateway] [ioEventLoop-5-1] [ ] o.s.c.g.f.ratelimit.RedisRateLimiter : Error calling rate limiter lua

org.springframework.data.redis.RedisSystemException: Error in execution
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)
at org.springframework.data.redis.connection.lettuce.LettuceReactiveRedisConnection.lambda$translateException$0(LettuceReactiveRedisConnection.java:242)
at reactor.core.publisher.Flux.lambda$onErrorMap$27(Flux.java:7267)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onError(MonoFlatMapMany.java:256)
at io.lettuce.core.RedisPublisher$ImmediateSubscriber.onError(RedisPublisher.java:895)
at io.lettuce.core.RedisPublisher$State.onError(RedisPublisher.java:716)
at io.lettuce.core.RedisPublisher$RedisSubscription.onError(RedisPublisher.java:357)
at io.lettuce.core.RedisPublisher$SubscriptionCommand.onError(RedisPublisher.java:801)
at io.lettuce.core.RedisPublisher$SubscriptionCommand.doOnComplete(RedisPublisher.java:761)
at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:65)
at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:63)
at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:745)
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:680)
at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:597)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1475)
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1338)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1387)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:469)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: io.lettuce.core.RedisCommandExecutionException: ERR This Redis command is not allowed from script script: 775635cca91092477cbcee85fd800accb6adc4b2, on @user_script:17.
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:147)
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:116)
... 31 common frames omitted

@sastie-rai
Copy link
Author

This is using spring-cloud-gateway 4.1.3 on java 21 and spring boot 3.2.5

@spencergibb
Copy link
Member

If I'm reading the error correctly, this is the line in question https://github.com/spring-cloud/spring-cloud-gateway/blob/main/spring-cloud-gateway-server%2Fsrc%2Fmain%2Fresources%2FMETA-INF%2Fscripts%2Frequest_rate_limiter.lua#L17

I could be wrong though. I'm not sure what we could do any differently. @mp911de thoughts?

@sastie-rai
Copy link
Author

@spencergibb I did some digging, KEYS must be the culprit.
elasticache serverless redis makes KEYS unavailable:
https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/SupportedCommands.html#RestrictedCommandsRedis
they only allow SCAN

@spencergibb
Copy link
Member

I suppose we document the required redis commands

@sastie-rai
Copy link
Author

sastie-rai commented Apr 30, 2024

@spencergibb, looking at the lua file you linked, the only unavailable command in AWS elasticache redis (serverless) is KEYS. So until they decide to support KEYS, this is dead in the water.

@spencergibb
Copy link
Member

I don't know redis well enough to know if there is a replacement

@sastie-rai
Copy link
Author

Lua scripting within Redis (e.g., via EVAL commands) doesn't natively support iterative commands like SCAN because scripts must run atomically without interruption. Directly adapting KEYS to SCAN inside a Lua script executed in Redis isn’t feasible because you can't persistently store state (like a cursor) between script executions, and scripts should not run non-deterministic commands like SCAN that might return different results on each execution.

I will have to find a different backend for the rate limiter.

@spencergibb
Copy link
Member

spencergibb commented Apr 30, 2024

See #2955, bucket4j has multiple persistence options

@spring-cloud-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-cloud-issues
Copy link

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants