alisaifee / limits

Rate limiting using various strategies and storage backends such as redis & memcached
https://limits.readthedocs.org
MIT License
410 stars 58 forks source link

Add support for async backend clients #60

Closed laurentS closed 2 years ago

laurentS commented 4 years ago

In order to make slowapi asyncio compatible, I would need to update the layers underneath to work asynchronously. I think the key part is getting limits to support asyncio.

Would you be interested in me having a go at it? I think it would be possible to either:

And then add an async storage backend, starting with redis.

Otherwise, if you see no interest in having this in the same code base, I guess I could fork the library and do more or less the same, but I think it's probably more valuable to have everything together.

alisaifee commented 4 years ago

I'd be happy to add this.

I haven't had a lot of experience working with asyncio and haven't quite figured how to achieve this without duplicating most of the Storage implementations. Regarding the API my initial thoughts are to have a separate namespace for the rate limit strategies - limits.async.strategies & limits.async.storage and additionally a new helper method async_storage_from_string.

From some quick research the following client libraries seem like good candidates:

Do you have any opinions on these or know of any other options?

Lastly, this will invariably end up in v2.0 which will be where I'll drop python2.x support and remove some of the legacy stuff like support for gae memcached.

laurentS commented 4 years ago

Unfortunately, my feeling is similar with regards to duplicating the code. I've looked at a few examples of other libraries that offer both sync/async implementations, and a lot of it looks like copy/paste of entire functions. I guess this is really only needed for the part of the code that actually triggers blocking calls to the backends. Init code and such might not need duplicating. and we might manage to factor out the logic into a shared method, and keep the actual call to the storage in separate places.

Regarding redis clients, I did have a quick look as well. I initially thought of aioredis but found an issue on django's channels repo talking about moving away from it because the authors seem to have more or less abandoned the project for a few months now. They seem to want to move towards aredis, so it might be smart to follow the crowd, as it'll bring a bunch of extra eyes on the code there, I suppose?

I also came across walrus which is actively maintained but is probably a bit too spread out in terms of functionality for this purpose.

I will try to create the async strategies and storage under a separate namespace as you suggest, and I guess we'll see if and how we can keep some of the code shared between the two sides. Dropping python2.7 will definitely help with that :)

FesonX commented 2 years ago

Unfortunately, my feeling is similar with regards to duplicating the code. I've looked at a few examples of other libraries that offer both sync/async implementations, and a lot of it looks like copy/paste of entire functions. I guess this is really only needed for the part of the code that actually triggers blocking calls to the backends. Init code and such might not need duplicating. and we might manage to factor out the logic into a shared method, and keep the actual call to the storage in separate places.

Regarding redis clients, I did have a quick look as well. I initially thought of aioredis but found an issue on django's channels repo talking about moving away from it because the authors seem to have more or less abandoned the project for a few months now. They seem to want to move towards aredis, so it might be smart to follow the crowd, as it'll bring a bunch of extra eyes on the code there, I suppose?

I also came across walrus which is actively maintained but is probably a bit too spread out in terms of functionality for this purpose.

I will try to create the async strategies and storage under a separate namespace as you suggest, and I guess we'll see if and how we can keep some of the code shared between the two sides. Dropping python2.7 will definitely help with that :)

The aioredis alive again! Any plan to support aioredis as storage backends? Migration to aioredis 2.0 #930

alisaifee commented 2 years ago

This is now available in a pre-release (2.1.0bN) and contains support for redis, memcached & mongodb. The support is limited to python 3.7, 3.8 & 3.9 as both the redis & memcached dependencies are not installable / functional on python 3.10 yet.

I'll soon be making a 2.1.0 release which contains these features - but you can take them for a ride already by installing for example:

pip install limits[async-redis]==2.1.0b3