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

Clustered rate limiters #5800

Closed
Tracked by #5769
mperham opened this issue Feb 21, 2023 · 8 comments
Closed
Tracked by #5769

Clustered rate limiters #5800

mperham opened this issue Feb 21, 2023 · 8 comments
Milestone

Comments

@mperham
Copy link
Collaborator

mperham commented Feb 21, 2023

If using many thousands or even millions of rate limiters, it can be hugely beneficial to use a Cluster as the rate limiter storage in order to scale beyond one Redis instance. We can modify the rate limiter data model to take advantage of "Hash tags" in the cluster spec1 to ensure all keys for a limiter X resides on the same Redis instance within the cluster. This ensures that any related Lua script will have local data to work with when running within Redis.

Note that the Web UI functionality is not cluster-friendly; it uses SCAN to look for all limiters to list but but this will only scan one Redis instance. Nevertheless, for the largest users this limitation on visibility (who's using the Web UI to look at millions of limiters??) is a reasonable trade off to enable massive scale.

cc @sj26

Footnotes

  1. https://redis.io/docs/reference/cluster-spec/

@mperham mperham modified the milestones: 7.0, 7.1 Feb 21, 2023
@mperham
Copy link
Collaborator Author

mperham commented Feb 21, 2023

This is a harder change than I first thought; I don't have a Redis cluster to test against so I need to create some non-trivial test infrastructure first.

@mperham
Copy link
Collaborator Author

mperham commented Feb 21, 2023

  • Concurrent - uses multiple keys, needs hash tagging
  • Bucket - uses multiple keys, needs hash tagging
  • Window - only uses one key, naturally cluster-safe
  • Leaky - only uses one key, naturally cluster-safe
  • Points - only uses one key, naturally cluster-safe

@mperham
Copy link
Collaborator Author

mperham commented Feb 21, 2023

Good blog post on this subject: https://redis.com/blog/redis-clustering-best-practices-with-keys/

@jgaskins
Copy link

Note that the Web UI functionality is not cluster-friendly; it uses SCAN to look for all limiters to list but but this will only scan one Redis instance.

The way I implemented this in my Crystal Redis library was to have Cluster#scan_each run scan_each across one replica of each shard. IIRC the Ruby Redis library doesn't have that, and it may not be a great idea at a large-enough scale, but it could still work for this purpose.

@mperham
Copy link
Collaborator Author

mperham commented Feb 21, 2023

@jgaskins Yep. The Pro Web UI already has a sharding support; the customer could set up the Web UI with each cluster member as a shard.

@sj26
Copy link
Contributor

sj26 commented Feb 21, 2023

If it's helpful, I started a cluster for testing with a Procfile containing:

redis-node-1: cd tmp/redis-cluster/node-1 && redis-server --port 7001 --cluster-enabled yes --cluster-config-file node.conf --cluster-node-timeout 5000 --appendonly yes
redis-node-2: cd tmp/redis-cluster/node-2 && redis-server --port 7002 --cluster-enabled yes --cluster-config-file node.conf --cluster-node-timeout 5000 --appendonly yes
redis-node-3: cd tmp/redis-cluster/node-3 && redis-server --port 7003 --cluster-enabled yes --cluster-config-file node.conf --cluster-node-timeout 5000 --appendonly yes

and then running:

redis-cli --cluster create 127.0.0.1:700{1,2,3} --cluster-replicas 0

The config files mentioned do not need to exist, they are written by redis during the cluster setup command.

In CI, we use a docker-compose containing:

  # This creates a redis cluster with 3 master nodes and 3 replicas, as
  # recommended in https://redis.io/docs/manual/scaling.
  # It's likely the replicas aren't necessary in test environments but I don't
  # see any harm including them just to make CI that little bit closer to
  # production.
  #
  # Clustered redis expects to have a public IP address and port that's used
  # by the other nodes as well as clients. This is fiddly to get right in
  # docker so we use a third party docker image that does that work for us.
  # See https://github.com/bitnami/bitnami-docker-redis-cluster.
  redis-node-0:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'ALLOW_EMPTY_PASSWORD=true'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
      - 'REDIS_CLUSTER_CREATOR=yes'
      - 'REDIS_CLUSTER_REPLICAS=1'
    depends_on:
      - redis-node-1
      - redis-node-2
      - redis-node-3
      - redis-node-4
      - redis-node-5

  redis-node-1:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'ALLOW_EMPTY_PASSWORD=true'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-2:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'ALLOW_EMPTY_PASSWORD=true'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-3:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'ALLOW_EMPTY_PASSWORD=true'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-4:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'ALLOW_EMPTY_PASSWORD=true'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

  redis-node-5:
    image: docker.io/bitnami/redis-cluster:7.0
    environment:
      - 'ALLOW_EMPTY_PASSWORD=true'
      - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'

@sj26
Copy link
Contributor

sj26 commented Feb 22, 2023

IIRC the Ruby Redis library doesn't have that

There's a separate cluster client which implements it on top of the new redis-client gem:

https://github.com/redis-rb/redis-cluster-client

(Just interesting, not really relevant to this particular issue.)

@mperham mperham closed this as completed Mar 7, 2023
@mperham mperham mentioned this issue Mar 7, 2023
10 tasks
@sj26
Copy link
Contributor

sj26 commented Mar 7, 2023

💚

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

No branches or pull requests

3 participants