Skip to content

Commit 8fcd208

Browse files
authored
Initial support for Redis cluster client. (#52)
1 parent 6e7f456 commit 8fcd208

File tree

12 files changed

+447
-9
lines changed

12 files changed

+447
-9
lines changed

.github/workflows/test-cluster.yaml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Test Cluster
2+
3+
on: [push, pull_request]
4+
5+
permissions:
6+
contents: read
7+
8+
env:
9+
CONSOLE_OUTPUT: XTerm
10+
11+
jobs:
12+
test:
13+
name: ${{matrix.ruby}} on ${{matrix.os}}
14+
runs-on: ${{matrix.os}}-latest
15+
continue-on-error: ${{matrix.experimental}}
16+
17+
strategy:
18+
matrix:
19+
os:
20+
- ubuntu
21+
22+
ruby:
23+
- "3.1"
24+
- "3.2"
25+
- "3.3"
26+
27+
experimental: [false]
28+
29+
steps:
30+
- uses: actions/checkout@v4
31+
32+
- name: Install Docker Compose
33+
run: |
34+
sudo apt-get update
35+
sudo apt-get install -y docker-compose
36+
37+
- name: Run tests
38+
timeout-minutes: 10
39+
env:
40+
RUBY_VERSION: ${{matrix.ruby}}
41+
run: docker-compose -f cluster/docker-compose.yaml up --exit-code-from tests

.github/workflows/test-coverage.yaml

+32
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,42 @@ jobs:
8080
name: coverage-${{matrix.os}}-${{matrix.ruby}}
8181
path: .covered.db
8282

83+
test-cluster:
84+
name: ${{matrix.ruby}} on ${{matrix.os}} (Cluster)
85+
runs-on: ${{matrix.os}}-latest
86+
87+
strategy:
88+
matrix:
89+
os:
90+
- ubuntu
91+
92+
ruby:
93+
- "3.3"
94+
95+
steps:
96+
- uses: actions/checkout@v4
97+
98+
- name: Install Docker Compose
99+
run: |
100+
sudo apt-get update
101+
sudo apt-get install -y docker-compose
102+
103+
- name: Run tests
104+
timeout-minutes: 10
105+
env:
106+
RUBY_VERSION: ${{matrix.ruby}}
107+
run: docker-compose -f cluster/docker-compose.yaml up --exit-code-from tests
108+
109+
- uses: actions/upload-artifact@v3
110+
with:
111+
name: coverage-${{matrix.os}}-${{matrix.ruby}}
112+
path: .covered.db
113+
83114
validate:
84115
needs:
85116
- test
86117
- test-sentinel
118+
- test-cluster
87119
runs-on: ubuntu-latest
88120

89121
steps:

cluster/docker-compose.yaml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
services:
2+
redis-a:
3+
image: redis
4+
command: redis-server /etc/redis/redis.conf
5+
volumes:
6+
- ./node-a/cluster.conf:/etc/redis/redis.conf
7+
redis-b:
8+
image: redis
9+
command: redis-server /etc/redis/redis.conf
10+
volumes:
11+
- ./node-b/cluster.conf:/etc/redis/redis.conf
12+
redis-c:
13+
image: redis
14+
command: redis-server /etc/redis/redis.conf
15+
volumes:
16+
- ./node-c/cluster.conf:/etc/redis/redis.conf
17+
controller:
18+
image: redis
19+
command: >
20+
bash -c "
21+
redis-cli --cluster create --cluster-yes --cluster-replicas 0 redis-a:6379 redis-b:6379 redis-c:6379;
22+
while true; do
23+
redis-cli -h redis-a cluster info | grep cluster_state:fail;
24+
sleep 1;
25+
done"
26+
healthcheck:
27+
test: "redis-cli -h redis-a cluster info | grep cluster_state:ok"
28+
interval: 1s
29+
timeout: 3s
30+
retries: 30
31+
depends_on:
32+
- redis-a
33+
- redis-b
34+
- redis-c
35+
tests:
36+
image: ruby:${RUBY_VERSION:-latest}
37+
volumes:
38+
- ../:/code
39+
command: bash -c "cd /code && bundle install && bundle exec sus cluster/test"
40+
environment:
41+
- COVERAGE=${COVERAGE}
42+
depends_on:
43+
- controller

cluster/node-a/cluster.conf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cluster-enabled yes
2+
cluster-config-file nodes.conf
3+
cluster-node-timeout 5000
4+
appendonly yes

cluster/node-b/cluster.conf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cluster-enabled yes
2+
cluster-config-file nodes.conf
3+
cluster-node-timeout 5000
4+
appendonly yes

cluster/node-c/cluster.conf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cluster-enabled yes
2+
cluster-config-file nodes.conf
3+
cluster-node-timeout 5000
4+
appendonly yes

cluster/readme.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Cluster Testing
2+
3+
To test clusters, you need to set up three redis instances (shards) and bind them together into a cluster.
4+
5+
## Running Tests
6+
7+
``` bash
8+
$ cd cluster
9+
$ docker-compose up tests
10+
[+] Running 5/0
11+
✔ Container cluster-redis-b-1 Running
12+
✔ Container cluster-redis-c-1 Running
13+
✔ Container cluster-redis-a-1 Running
14+
✔ Container cluster-controller-1 Running
15+
✔ Container cluster-tests-1 Created
16+
Attaching to tests-1
17+
tests-1 | Bundle complete! 13 Gemfile dependencies, 41 gems now installed.
18+
tests-1 | Use `bundle info [gemname]` to see where a bundled gem is installed.
19+
tests-1 | 6 installed gems you directly depend on are looking for funding.
20+
tests-1 | Run `bundle fund` for details
21+
tests-1 | 0 assertions
22+
tests-1 | 🏁 Finished in 4.9ms; 0.0 assertions per second.
23+
tests-1 | 🐇 No slow tests found! Well done!
24+
tests-1 exited with code 0
25+
```
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
3+
# Released under the MIT License.
4+
# Copyright, 2024, by Samuel Williams.
5+
6+
require 'async/redis/cluster_client'
7+
require 'sus/fixtures/async'
8+
require 'securerandom'
9+
10+
describe Async::Redis::ClusterClient do
11+
include Sus::Fixtures::Async::ReactorContext
12+
13+
let(:node_a) {"redis://redis-a:6379"}
14+
let(:node_b) {"redis://redis-b:6379"}
15+
let(:node_c) {"redis://redis-c:6379"}
16+
17+
let(:endpoints) {[
18+
Async::Redis::Endpoint.parse(node_a),
19+
Async::Redis::Endpoint.parse(node_b),
20+
Async::Redis::Endpoint.parse(node_c)
21+
]}
22+
23+
let(:cluster) {subject.new(endpoints)}
24+
25+
let(:key) {"cluster-test:fixed"}
26+
let(:value) {"cluster-test-value"}
27+
28+
it "can get a client for a given key" do
29+
slot = cluster.slot_for(key)
30+
client = cluster.client_for(slot)
31+
32+
expect(client).not.to be_nil
33+
end
34+
35+
it "can get and set values" do
36+
result = nil
37+
38+
cluster.clients_for(key) do |client|
39+
client.set(key, value)
40+
41+
result = client.get(key)
42+
end
43+
44+
expect(result).to be == value
45+
end
46+
end

lib/async/redis.rb

+2
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66

77
require_relative 'redis/version'
88
require_relative 'redis/client'
9+
10+
require_relative 'redis/cluster_client'
911
require_relative 'redis/sentinel_client'

0 commit comments

Comments
 (0)