fastify / fastify-rate-limit

A low overhead rate limiter for your routes
MIT License
504 stars 71 forks source link

recommend enableAutoPipelining? #257

Open Uzlopak opened 2 years ago

Uzlopak commented 2 years ago

Prerequisites

Issue

I wonder if we get some performance benefit if we recommend enableAutoPipelining?

mcollina commented 2 years ago

Likely so

dancastillo commented 1 year ago

would this just be a documentation update? Seeing as option is available in ioredis https://github.com/luin/ioredis/blob/main/lib/redis/RedisOptions.ts#L136

Uzlopak commented 1 year ago

I dont think this is a documentation only task.

I assume that for really using autoPipelining, we have to replace the manually set pipelines with a lua script. Something like https://github.com/wyattjoh/rate-limit-redis/blob/main/src/lib.ts#L58

Then I think we can make use of autoPipelining.

mcollina commented 1 year ago

If we enable autopipelining we can just remove those two pipeline commands.

Uzlopak commented 1 year ago

Sure, but what if the implementator does not use ioredis but node-redis or something like that. In the readme.md we say, that we recommend ioredis, but other redis clients are also possible. And if they dont implement autopipelining then we have a broken solution.

climba03003 commented 1 year ago

Auto-pipelining meaning they will defer some of the operation and send them in groups in unexpected way. It also means that we are actually defering the request to be process since rate-limit should happen in the earliest stage.

I don't think how the impact was, maybe it will increase latency but result in higher throughput. The change of default should be carefully considered and MUST be benchmark.

Uzlopak commented 1 year ago

Probably using lua improves the perforrmance. It would also mean that we dont need to pipeline in our redis store. It is then optional to use auto pipelining or not.

mcollina commented 1 year ago

Auto-pipelining meaning they will defer some of the operation and send them in groups.

This is incorrect. In fact, it will execute ops sooner because they will be batched with previous ops.

In my experience using Lua does not improve things in similar cases. The cost here is driven by head-of-line blocking.

climba03003 commented 1 year ago

This is incorrect. In fact, it will execute ops sooner because they will be batched with previous ops.

I am not sure about this statement, when I look through the code of ioredies it should be delaying the network connection in order to stack batch of command.

https://github.com/luin/ioredis/blob/e41c3dc880906e8aad73332837bf233f65d12e67/lib/autoPipelining.ts#L144-L175

When it do not use autoPipelining, the connection is not limit to 1. It actually send concurrently.

autoPipelining seems to increase throughput only because it reduce numbers TCP handshake.

mcollina commented 1 year ago

I invented that tecnique, watch the video: https://m.youtube.com/watch?v=0L0ER4pZbX4.

Only one command could be in flight on a socket connected to Redis. Without autopipelining, all commands are queued and run in order. With autopipelining:

  1. If the socket has not any commands in flight, the command is deferred by 1 macrotick (wait for other command in the same event loop run).
  2. if the socket has a command in flight, all subsequent commands are batched and sent as single pipeline.

This mitigate head of line blocking in Redis. Considering 100 commands and 10ms RTT this will give a save of 990ms waiting time. Pretty good.

Uzlopak commented 1 year ago

I guess we can check if the redis option is of ioredis instance and if so check if autoPipeline is enabled. I so use without pipeline() if autoPipelin is true.

gurgunday commented 1 year ago

node-redis seems to have built-in autoPipelining, which could help users not think about these kinds of things

https://github.com/redis/node-redis#auto-pipelining

I'm not sure what we should do here, I honestly don't know which one is better, or if that's a wrong way to look at it, which one is Redis Ltd's preferred/main package for node

Uzlopak commented 1 year ago

@gurgunday

The problem is, that we cant use autoPipelining, because our redis store is creating a pipeline for each call. So autoPipelining wont pipeline that again.

I think we have to create a custom command in redis, which does the incr and pttl call, so we dont use pipeline anymore and autoPipeline takes effect.

gurgunday commented 1 week ago

Just an update here, the final status of this is that since we now do 1 command per request with lua, autoPipelining didn't improve the performance

We'd talked about it with @Uzlopak at some point, but I don't know where it was