vutran1710/PyrateLimiter

'CROSSSLOT Keys in request don't hash to the same slot' when using Redis Cluster

Closed this issue · 6 comments

I have a fairly simple code that keeps getting this exception

redis_db = Redis(host='MYHOST', port=6379, ssl=True, ssl_cert_reqs="none")
bucket = RedisBucket.init([Rate(1, Duration.SECOND.value)], redis_db, 'ratelimit')
limiter = Limiter(bucket, max_delay=Duration.HOUR)
deco = limiter.as_decorator()

@deco(lambda _: ('identity',1))
def sync_func(i):
    print(i)

for i in range(5):
    sync_func(i)

the exception:

File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/pyrate_limiter/limiter.py", line 315, in wrapper
    accquire_ok = self.try_acquire(name, weight)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/pyrate_limiter/limiter.py", line 286, in try_acquire
    result = self.handle_bucket_put(bucket, item)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/pyrate_limiter/limiter.py", line 235, in handle_bucket_put
    acquire = bucket.put(item)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/pyrate_limiter/buckets/redis_bucket.py", line 135, in put
    failing_rate = self._check_and_insert(item)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/pyrate_limiter/buckets/redis_bucket.py", line 117, in _check_and_insert
    idx = self.redis.evalsha(self.script_hash, len(keys), *keys, *args)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/commands/core.py", line 5307, in evalsha
    return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/commands/core.py", line 5291, in _evalsha
    return self.execute_command(command, sha, numkeys, *keys_and_args)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/client.py", line 508, in execute_command
    return conn.retry.call_with_retry(
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/retry.py", line 46, in call_with_retry
    return do()
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/client.py", line 509, in <lambda>
    lambda: self._send_command_parse_response(
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/client.py", line 485, in _send_command_parse_response
    return self.parse_response(conn, command_name, **options)
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/client.py", line 525, in parse_response
    response = connection.read_response()
  File "/Users/tomer/dev/projects/common/venv/lib/python3.8/site-packages/redis/connection.py", line 516, in read_response
    raise response
redis.exceptions.ResponseError: CROSSSLOT Keys in request don't hash to the same slot

looks like you are using redis in cluster mode.

maybe you can try resolve the problem following this tutorial here?

https://hackernoon.com/resolving-the-crossslot-keys-error-with-redis-cluster-mode-enabled

still cant get it to work

redis_db = Redis(host='MYHOST', port=6379, ssl=True, ssl_cert_reqs="none")
bucket = RedisBucket.init([Rate(1, Duration.SECOND.value)], redis_db, '{ratelimit}:abc')
limiter = Limiter(bucket, max_delay=Duration.HOUR)
deco = limiter.as_decorator()

@deco(lambda _: ('{ratelimit}:abc',1))
def sync_func(i):
    print(i)

@vutran1710 i created new redis with cluster mode disabled but having an issue with rate limiting async requests
when using "regular" redis it just sends all the request at once
when using async redis it fails due to bucket full even though i set long max_delay

from redis.asyncio import Redis as AsyncRedis

async def create_limiter(*rates: Rate, identity):
    redis_db = AsyncRedis(host=endpoint, port=6379, ssl=True, ssl_cert_reqs="none")
    bucket_init_method = lambda: RedisBucket.init(rates, redis_db, identity)
    bucket = await RedisBucket.init(rates, redis_db, identity)
    limiter = Limiter(bucket, max_delay=Duration.HOUR)
    deco = limiter.as_decorator()
    return deco(lambda _: (identity,1))

async def main():
    mylimiter = await create_limiter(Rate(1, 1), identity='myident', bucket_class=BucketClass.ASYNC_REDIS)

    @mylimiter
    async def async_func(i):
        print(i)

    tasks = [asyncio.create_task(async_func(i)) for i in range(5)]
    await asyncio.gather(*tasks)

    asyncio.run(main())

getting this:

pyrate_limiter.exceptions.BucketFullException: Bucket for item=myident with Rate limit=1/1.0s is already full

interesting.
ill take a look at this soon

@vutran1710 any news on this issue?

I opened a PR that fixed the issue for me:
#151