Skip to content

Commit 03f04d3

Browse files
Add more guides.
1 parent e7ce35c commit 03f04d3

File tree

12 files changed

+996
-83
lines changed

12 files changed

+996
-83
lines changed

.rubocop.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins:
2+
- rubocop-md
23
- rubocop-socketry
34

45
AllCops:

context/getting-started.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ $ bundle add async-redis
1515
### Basic Local Connection
1616

1717
``` ruby
18-
require 'async/redis'
18+
require "async/redis"
1919

2020
Async do
2121
endpoint = Async::Redis.local_endpoint(
@@ -42,7 +42,7 @@ You can also encode this information in a URL:
4242
This example demonstrates parsing an environment variable with a `redis://` or SSL `rediss://` scheme, and demonstrates how you can specify SSL parameters on the SSLContext object.
4343

4444
``` ruby
45-
require 'async/redis'
45+
require "async/redis"
4646

4747
ssl_context = OpenSSL::SSL::SSLContext.new.tap do |context|
4848
# Load the certificate store:
@@ -75,15 +75,15 @@ end
7575
### Variables
7676

7777
``` ruby
78-
require 'async'
79-
require 'async/redis'
78+
require "async"
79+
require "async/redis"
8080

8181
endpoint = Async::Redis.local_endpoint
8282
client = Async::Redis::Client.new(endpoint)
8383

8484
Async do
85-
client.set('X', 10)
86-
puts client.get('X')
85+
client.set("X", 10)
86+
puts client.get("X")
8787
ensure
8888
client.close
8989
end

context/subscriptions.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ The `SUBSCRIBE` command is used to subscribe to one or more channels. When a mes
1313
First, let's create a simple listener that subscribes to messages on a channel:
1414

1515
``` ruby
16-
require 'async'
17-
require 'async/redis'
16+
require "async"
17+
require "async/redis"
1818

1919
client = Async::Redis::Client.new
2020

2121
Async do
22-
client.subscribe 'status.frontend' do |context|
22+
client.subscribe "status.frontend" do |context|
2323
puts "Listening for messages on 'status.frontend'..."
2424

2525
type, name, message = context.listen
@@ -32,14 +32,14 @@ end
3232
Now, let's create a publisher that sends messages to the same channel:
3333

3434
``` ruby
35-
require 'async'
36-
require 'async/redis'
35+
require "async"
36+
require "async/redis"
3737

3838
client = Async::Redis::Client.new
3939

4040
Async do
4141
puts "Publishing message..."
42-
client.publish 'status.frontend', 'good'
42+
client.publish "status.frontend", "good"
4343
puts "Message sent!"
4444
end
4545
```
@@ -57,13 +57,13 @@ Received: good
5757
Subscriptions are at-most-once delivery. In addition, subscriptions are stateful, meaning that they maintain their own internal state and can be affected by network issues or server restarts. In order to improve resilience, it's important to implement error handling and reconnection logic.
5858

5959
```ruby
60-
require 'async'
61-
require 'async/redis'
60+
require "async"
61+
require "async/redis"
6262

6363
client = Async::Redis::Client.new
6464

6565
Async do
66-
client.subscribe 'status.frontend' do |context|
66+
client.subscribe "status.frontend" do |context|
6767
puts "Listening for messages on 'status.frontend'..."
6868

6969
context.each do |type, name, message|
@@ -84,14 +84,14 @@ The `PSUBSCRIBE` command is used to subscribe to channels that match a given pat
8484
Let's replace the receiver in the above example:
8585

8686
``` ruby
87-
require 'async'
88-
require 'async/redis'
87+
require "async"
88+
require "async/redis"
8989

9090
endpoint = Async::Redis.local_endpoint
9191
client = Async::Redis::Client.new(endpoint)
9292

9393
Async do
94-
client.psubscribe 'status.*' do |context|
94+
client.psubscribe "status.*" do |context|
9595
puts "Listening for messages on 'status.*'..."
9696

9797
type, pattern, name, message = context.listen
@@ -110,14 +110,14 @@ If you are working with a clustered environment, you can improve performance by
110110
To use sharded subscriptions, use a cluster client which supports sharded pub/sub:
111111

112112
``` ruby
113-
require 'async'
114-
require 'async/redis'
113+
require "async"
114+
require "async/redis"
115115

116116
# endpoints = ...
117117
cluster_client = Async::Redis::ClusterClient.new(endpoints)
118118

119119
Async do
120-
cluster_client.subscribe 'status.frontend' do |context|
120+
cluster_client.subscribe "status.frontend" do |context|
121121
puts "Listening for messages on 'status.frontend'..."
122122

123123
type, name, message = context.listen
@@ -128,15 +128,15 @@ end
128128
```
129129

130130
``` ruby
131-
require 'async'
132-
require 'async/redis'
131+
require "async"
132+
require "async/redis"
133133

134134
# endpoints = ...
135135
cluster_client = Async::Redis::ClusterClient.new(endpoints)
136136

137137
Async do
138138
puts "Publishing message..."
139-
cluster_client.publish('status.frontend', 'good')
139+
cluster_client.publish("status.frontend", "good")
140140
puts "Message sent!"
141141
end
142142
```

gems.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
gem "decode"
2626

2727
gem "rubocop"
28+
gem "rubocop-md"
2829
gem "rubocop-socketry"
2930

3031
gem "bake-test"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Client Architecture
2+
3+
This guide explains the different client types available in `async-redis` and when to use each one.
4+
5+
## Redis Deployment Patterns
6+
7+
Redis can be deployed in several configurations, each serving different scalability and availability needs:
8+
9+
### Single Instance
10+
A single Redis server handles all operations. Simple to set up and manage, but limited by the capacity of one machine.
11+
12+
**Use when:**
13+
- **Development**: Local development and testing.
14+
- **Small applications**: Low traffic applications with simple caching needs.
15+
- **Prototyping**: Getting started quickly without infrastructure complexity.
16+
17+
**Limitations:**
18+
- **Single point of failure**: If Redis goes down, your application loses caching.
19+
- **Memory constraints**: Limited by the memory of one machine.
20+
- **CPU bottlenecks**: All operations processed by one Redis instance.
21+
22+
Use {ruby Async::Redis::Client} to connect to a single Redis instance.
23+
24+
``` ruby
25+
require "async/redis"
26+
27+
endpoint = Async::Redis.local_endpoint
28+
client = Async::Redis::Client.new(endpoint)
29+
30+
Async do
31+
begin
32+
client.set("cache:page", "cached content")
33+
content = client.get("cache:page")
34+
puts "Retrieved: #{content}"
35+
ensure
36+
client.close
37+
end
38+
end
39+
```
40+
41+
### Cluster (Sharded)
42+
43+
Multiple Redis nodes work together, with data automatically distributed across nodes based on key hashing. Provides horizontal scaling and high availability.
44+
45+
**Use when:**
46+
- **Large datasets**: Data doesn't fit in a single Redis instance's memory.
47+
- **High throughput**: Need to distribute load across multiple machines.
48+
- **Horizontal scaling**: Want to add capacity by adding more nodes.
49+
50+
**Benefits:**
51+
- **Automatic sharding**: Data distributed across nodes based on consistent hashing.
52+
- **High availability**: Cluster continues operating if some nodes fail.
53+
- **Linear scaling**: Add nodes to increase capacity and throughput.
54+
55+
Use {ruby Async::Redis::ClusterClient} to connect to a Redis cluster.
56+
57+
``` ruby
58+
require "async/redis"
59+
60+
cluster_endpoints = [
61+
Async::Redis::Endpoint.new(hostname: "redis-1.example.com", port: 7000),
62+
Async::Redis::Endpoint.new(hostname: "redis-2.example.com", port: 7001),
63+
Async::Redis::Endpoint.new(hostname: "redis-3.example.com", port: 7002)
64+
]
65+
66+
cluster_client = Async::Redis::ClusterClient.new(cluster_endpoints)
67+
68+
Async do
69+
begin
70+
# Data automatically distributed across nodes:
71+
cluster_client.set("cache:user:123", "user data")
72+
cluster_client.set("cache:user:456", "other user data")
73+
74+
data = cluster_client.get("cache:user:123")
75+
puts "Retrieved from cluster: #{data}"
76+
ensure
77+
cluster_client.close
78+
end
79+
end
80+
```
81+
82+
Note that the cluster client automatically routes requests to the correct shard where possible.
83+
84+
### Sentinel (Master/Slave with Failover)
85+
86+
One master handles writes, multiple slaves handle reads, with sentinel processes monitoring for automatic failover.
87+
88+
**Use when:**
89+
- **High availability**: Cannot tolerate Redis downtime.
90+
- **Read scaling**: Many read operations, fewer writes.
91+
- **Automatic failover**: Want automatic promotion of slaves to masters.
92+
93+
**Benefits:**
94+
- **Automatic failover**: Sentinels promote slaves when master fails.
95+
- **Read/write separation**: Distribute read load across slave instances.
96+
- **Monitoring**: Built-in health checks and failure detection.
97+
98+
Use {ruby Async::Redis::SentinelClient} to connect to a Redis sentinel.
99+
100+
``` ruby
101+
require "async/redis"
102+
103+
sentinel_endpoints = [
104+
Async::Redis::Endpoint.new(hostname: "sentinel-1.example.com", port: 26379),
105+
Async::Redis::Endpoint.new(hostname: "sentinel-2.example.com", port: 26379),
106+
Async::Redis::Endpoint.new(hostname: "sentinel-3.example.com", port: 26379)
107+
]
108+
109+
sentinel_client = Async::Redis::SentinelClient.new(
110+
sentinel_endpoints,
111+
master_name: "mymaster"
112+
)
113+
114+
Async do
115+
begin
116+
# Automatically connects to current master:
117+
sentinel_client.set("cache:critical", "important data")
118+
data = sentinel_client.get("cache:critical")
119+
puts "Retrieved from master: #{data}"
120+
ensure
121+
sentinel_client.close
122+
end
123+
end
124+
```

guides/getting-started/readme.md

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@ Add the gem to your project:
1010
$ bundle add async-redis
1111
```
1212

13+
## Core Concepts
14+
15+
`async-redis` has several core concepts:
16+
17+
- A {ruby Async::Redis::Client} which represents the main entry point for Redis operations.
18+
- An {ruby Async::Redis::Endpoint} which represents connection details including host, port, and authentication.
19+
- An {ruby Async::Redis::Context::Generic} which represents an actual connection to the Redis server.
20+
1321
## Usage
1422

15-
### Basic Local Connection
23+
This example shows how to connect to a local Redis server:
1624

1725
``` ruby
18-
require 'async/redis'
26+
require "async/redis"
1927

2028
Async do
29+
# Create a local endpoint with optional configuration:
2130
endpoint = Async::Redis.local_endpoint(
2231
# Optional database index:
2332
database: 1,
@@ -26,61 +35,56 @@ Async do
2635
)
2736

2837
client = Async::Redis::Client.new(endpoint)
38+
39+
# Get server information:
2940
puts client.info
3041

42+
# Store and retrieve a value:
3143
client.set("mykey", "myvalue")
3244
puts client.get("mykey")
45+
ensure
46+
# Always close the client to free resources:
47+
client&.close
3348
end
3449
```
3550

36-
### Connecting to Redis SSL Endpoint
51+
### Connecting to Redis using SSL
3752

3853
This example demonstrates parsing an environment variable with a `redis://` or SSL `rediss://` scheme, and demonstrates how you can specify SSL parameters on the SSLContext object.
3954

4055
``` ruby
41-
require 'async/redis'
56+
require "async/redis"
4257

43-
ssl_context = OpenSSL::SSL::SSLContext.new.tap do |context|
44-
# Load the certificate store:
45-
context.cert_store = OpenSSL::X509::Store.new.tap do |store|
46-
store.add_file(Rails.root.join("config/redis.pem").to_s)
47-
end
48-
49-
# Load the certificate:
50-
context.cert = OpenSSL::X509::Certificate.new(File.read(
51-
Rails.root.join("config/redis.crt")
52-
))
53-
54-
# Load the private key:
55-
context.key = OpenSSL::PKey::RSA.new(
56-
Rails.application.credentials.services.redis.private_key
57-
)
58-
59-
# Ensure the connection is verified according to the above certificates:
60-
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
61-
end
62-
63-
# e.g. REDIS_URL=rediss://:[email protected]:12345
64-
endpoint = Async::Redis::Endpoint.parse(ENV["REDIS_URL"], ssl_context: ssl_context)
58+
# Parse Redis URL with SSL support.
59+
# Example: REDIS_URL=rediss://:[email protected]:12345
60+
endpoint = Async::Redis::Endpoint.parse(ENV["REDIS_URL"])
6561
client = Async::Redis::Client.new(endpoint)
62+
6663
Sync do
6764
puts client.call("PING")
65+
ensure
66+
client&.close
6867
end
6968
```
7069

71-
### Variables
70+
Alternatively, you can parse a URL and pass credentials as options:
7271

7372
``` ruby
74-
require 'async'
75-
require 'async/redis'
73+
require "async/redis"
74+
75+
# Parse URL and add credentials:
76+
endpoint = Async::Redis::Endpoint.parse(
77+
"rediss://redis.example.com:6379",
78+
credentials: ["username", "password"]
79+
# Optional SSL context:
80+
# ssl_context: ...
81+
)
7682

77-
endpoint = Async::Redis.local_endpoint
7883
client = Async::Redis::Client.new(endpoint)
7984

8085
Async do
81-
client.set('X', 10)
82-
puts client.get('X')
86+
puts client.call("PING")
8387
ensure
84-
client.close
88+
client&.close
8589
end
8690
```

0 commit comments

Comments
 (0)