smart_message 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +38 -0
- data/.gitignore +5 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +35 -4
- data/README.md +169 -71
- data/Rakefile +29 -4
- data/docs/assets/images/ddq_architecture.svg +130 -0
- data/docs/assets/images/dlq_architecture.svg +115 -0
- data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
- data/docs/assets/images/enhanced-fluent-api.svg +149 -0
- data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
- data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
- data/docs/assets/images/fluent-api-demo.svg +59 -0
- data/docs/assets/images/performance-comparison.svg +161 -0
- data/docs/assets/images/redis-basic-architecture.svg +53 -0
- data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
- data/docs/assets/images/redis-queue-architecture.svg +101 -0
- data/docs/assets/images/smart_message.jpg +0 -0
- data/docs/assets/images/smart_message_walking.jpg +0 -0
- data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
- data/docs/assets/images/transport-comparison-matrix.svg +171 -0
- data/docs/assets/javascripts/mathjax.js +17 -0
- data/docs/assets/stylesheets/extra.css +51 -0
- data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
- data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
- data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
- data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
- data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
- data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
- data/docs/{examples.md → getting-started/examples.md} +115 -89
- data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
- data/docs/guides/redis-queue-getting-started.md +697 -0
- data/docs/guides/redis-queue-patterns.md +889 -0
- data/docs/guides/redis-queue-production.md +1091 -0
- data/docs/index.md +64 -0
- data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
- data/docs/{logging.md → reference/logging.md} +1 -1
- data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
- data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
- data/docs/{serializers.md → reference/serializers.md} +3 -5
- data/docs/{transports.md → reference/transports.md} +133 -11
- data/docs/transports/memory-transport.md +374 -0
- data/docs/transports/redis-enhanced-transport.md +524 -0
- data/docs/transports/redis-queue-transport.md +1304 -0
- data/docs/transports/redis-transport-comparison.md +496 -0
- data/docs/transports/redis-transport.md +509 -0
- data/examples/README.md +98 -5
- data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
- data/examples/city_scenario/README.md +515 -0
- data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
- data/examples/city_scenario/citizen.rb +195 -0
- data/examples/city_scenario/city_diagram.svg +125 -0
- data/examples/city_scenario/common/health_monitor.rb +80 -0
- data/examples/city_scenario/common/logger.rb +30 -0
- data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
- data/examples/city_scenario/fire_department.rb +446 -0
- data/examples/city_scenario/fire_emergency_flow.svg +95 -0
- data/examples/city_scenario/health_department.rb +100 -0
- data/examples/city_scenario/health_monitoring_system.svg +130 -0
- data/examples/city_scenario/house.rb +244 -0
- data/examples/city_scenario/local_bank.rb +217 -0
- data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
- data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
- data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
- data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
- data/examples/city_scenario/messages/health_check_message.rb +22 -0
- data/examples/city_scenario/messages/health_status_message.rb +35 -0
- data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
- data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
- data/examples/city_scenario/police_department.rb +316 -0
- data/examples/city_scenario/redis_monitor.rb +129 -0
- data/examples/city_scenario/redis_stats.rb +743 -0
- data/examples/city_scenario/room_for_improvement.md +240 -0
- data/examples/city_scenario/security_emergency_flow.svg +95 -0
- data/examples/city_scenario/service_internal_architecture.svg +154 -0
- data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
- data/examples/city_scenario/start_demo.sh +236 -0
- data/examples/city_scenario/stop_demo.sh +106 -0
- data/examples/city_scenario/visitor.rb +631 -0
- data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
- data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
- data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
- data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
- data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
- data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
- data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
- data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
- data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
- data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
- data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
- data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
- data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
- data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
- data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
- data/examples/memory/README.md +163 -0
- data/examples/memory/memory_transport_architecture.svg +90 -0
- data/examples/memory/point_to_point_pattern.svg +94 -0
- data/examples/memory/publish_subscribe_pattern.svg +125 -0
- data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
- data/examples/redis/README.md +230 -0
- data/examples/redis/alert_system_flow.svg +127 -0
- data/examples/redis/dashboard_status_flow.svg +107 -0
- data/examples/redis/device_command_flow.svg +113 -0
- data/examples/redis/redis_transport_architecture.svg +115 -0
- data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
- data/examples/redis/smart_home_system_architecture.svg +133 -0
- data/examples/redis_enhanced/README.md +319 -0
- data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/01_basic_messaging.rb +221 -0
- data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
- data/examples/redis_queue/02_pattern_routing.rb +405 -0
- data/examples/redis_queue/03_fluent_api.rb +422 -0
- data/examples/redis_queue/04_load_balancing.rb +486 -0
- data/examples/redis_queue/05_microservices.rb +735 -0
- data/examples/redis_queue/06_emergency_alerts.rb +777 -0
- data/examples/redis_queue/07_queue_management.rb +587 -0
- data/examples/redis_queue/README.md +366 -0
- data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/redis_queue_architecture.svg +148 -0
- data/ideas/README.md +41 -0
- data/ideas/agents.md +1001 -0
- data/ideas/database_transport.md +980 -0
- data/ideas/improvement.md +359 -0
- data/ideas/meshage.md +1788 -0
- data/ideas/message_discovery.md +178 -0
- data/ideas/message_schema.md +1381 -0
- data/lib/smart_message/.idea/.gitignore +8 -0
- data/lib/smart_message/.idea/markdown.xml +6 -0
- data/lib/smart_message/.idea/misc.xml +4 -0
- data/lib/smart_message/.idea/modules.xml +8 -0
- data/lib/smart_message/.idea/smart_message.iml +16 -0
- data/lib/smart_message/.idea/vcs.xml +6 -0
- data/lib/smart_message/addressing.rb +15 -0
- data/lib/smart_message/base.rb +0 -2
- data/lib/smart_message/configuration.rb +1 -1
- data/lib/smart_message/logger.rb +15 -4
- data/lib/smart_message/plugins.rb +5 -2
- data/lib/smart_message/serializer.rb +14 -0
- data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
- data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +34 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +5 -52
- data/mkdocs.yml +184 -0
- data/p2p_plan.md +326 -0
- data/p2p_roadmap.md +287 -0
- data/smart_message.gemspec +2 -0
- data/smart_message.svg +51 -0
- metadata +170 -44
- data/docs/README.md +0 -57
- data/examples/dead_letters.jsonl +0 -12
- data/examples/temp.txt +0 -94
- data/examples/tmux_chat/README.md +0 -283
- data/examples/tmux_chat/bot_agent.rb +0 -278
- data/examples/tmux_chat/human_agent.rb +0 -199
- data/examples/tmux_chat/room_monitor.rb +0 -160
- data/examples/tmux_chat/shared_chat_system.rb +0 -328
- data/examples/tmux_chat/start_chat_demo.sh +0 -190
- data/examples/tmux_chat/stop_chat_demo.sh +0 -22
- /data/docs/{properties.md → core-concepts/properties.md} +0 -0
- /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -0,0 +1,697 @@
|
|
1
|
+
# Redis Queue Transport - Getting Started
|
2
|
+
|
3
|
+
This guide will help you get started with SmartMessage's Redis Queue Transport, the most advanced transport offering RabbitMQ-style routing with Redis performance.
|
4
|
+
|
5
|
+
## Prerequisites
|
6
|
+
|
7
|
+
- Ruby 2.7 or higher
|
8
|
+
- Redis server running (localhost:6379 by default)
|
9
|
+
- SmartMessage gem installed
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add SmartMessage to your Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'smart_message', '~> 0.1.0'
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install directly:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install smart_message
|
23
|
+
```
|
24
|
+
|
25
|
+
Ensure Redis is running:
|
26
|
+
|
27
|
+
```bash
|
28
|
+
# Start Redis server
|
29
|
+
redis-server
|
30
|
+
|
31
|
+
# Test Redis connection
|
32
|
+
redis-cli ping
|
33
|
+
# Should return: PONG
|
34
|
+
```
|
35
|
+
|
36
|
+
## Quick Start
|
37
|
+
|
38
|
+
### 1. Basic Configuration
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
require 'smart_message'
|
42
|
+
|
43
|
+
# Configure SmartMessage to use Redis Queue transport
|
44
|
+
SmartMessage.configure do |config|
|
45
|
+
config.transport = :redis_queue
|
46
|
+
config.transport_options = {
|
47
|
+
url: 'redis://localhost:6379',
|
48
|
+
db: 0,
|
49
|
+
queue_prefix: 'myapp_queues',
|
50
|
+
consumer_group: 'myapp_workers'
|
51
|
+
}
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
### 2. Create Your First Message
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class WelcomeMessage < SmartMessage::Base
|
59
|
+
transport :redis_queue
|
60
|
+
|
61
|
+
property :user_name, required: true
|
62
|
+
property :email, required: true
|
63
|
+
property :signup_date, default: -> { Time.now.strftime('%Y-%m-%d') }
|
64
|
+
|
65
|
+
def process
|
66
|
+
puts "👋 Welcome #{user_name} (#{email})! Signed up: #{signup_date}"
|
67
|
+
|
68
|
+
# Your business logic here
|
69
|
+
UserMailer.welcome_email(email, user_name).deliver_now
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
### 3. Subscribe to Messages
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# Subscribe to all WelcomeMessage instances
|
78
|
+
WelcomeMessage.subscribe
|
79
|
+
|
80
|
+
puts "✅ Subscribed to welcome messages. Waiting for messages..."
|
81
|
+
|
82
|
+
# Keep the script running
|
83
|
+
sleep
|
84
|
+
```
|
85
|
+
|
86
|
+
### 4. Publish Messages
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# In another script or Rails console:
|
90
|
+
WelcomeMessage.new(
|
91
|
+
user_name: 'Alice Johnson',
|
92
|
+
email: 'alice@example.com'
|
93
|
+
).publish
|
94
|
+
|
95
|
+
puts "📤 Welcome message sent!"
|
96
|
+
```
|
97
|
+
|
98
|
+
## Understanding the Basics
|
99
|
+
|
100
|
+
### What Makes Redis Queue Different?
|
101
|
+
|
102
|
+
Unlike traditional Redis pub/sub, Redis Queue Transport provides:
|
103
|
+
|
104
|
+
1. **Persistent Queues**: Messages survive service restarts
|
105
|
+
2. **Load Balancing**: Multiple workers share message processing
|
106
|
+
3. **Pattern Routing**: Intelligent message routing like RabbitMQ
|
107
|
+
4. **Queue Management**: Monitor and manage message queues
|
108
|
+
|
109
|
+
### Routing Keys
|
110
|
+
|
111
|
+
Every message gets an enhanced routing key:
|
112
|
+
|
113
|
+
```
|
114
|
+
namespace.message_type.from_uuid.to_uuid
|
115
|
+
```
|
116
|
+
|
117
|
+
For example:
|
118
|
+
- `myapp.welcomemessage.signup_service.email_service`
|
119
|
+
- `myapp.ordermessage.api_gateway.payment_service`
|
120
|
+
|
121
|
+
## Basic Patterns
|
122
|
+
|
123
|
+
### 1. Simple Producer-Consumer
|
124
|
+
|
125
|
+
**Producer:**
|
126
|
+
```ruby
|
127
|
+
class TaskMessage < SmartMessage::Base
|
128
|
+
transport :redis_queue
|
129
|
+
|
130
|
+
property :task_id, required: true
|
131
|
+
property :task_type, required: true
|
132
|
+
property :priority, default: 'normal'
|
133
|
+
|
134
|
+
def process
|
135
|
+
puts "⚙️ Processing task #{task_id} [#{task_type}] - Priority: #{priority}"
|
136
|
+
|
137
|
+
# Simulate work
|
138
|
+
sleep(rand(1..3))
|
139
|
+
|
140
|
+
puts "✅ Task #{task_id} completed!"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Publish tasks
|
145
|
+
5.times do |i|
|
146
|
+
TaskMessage.new(
|
147
|
+
task_id: "TASK-#{sprintf('%03d', i + 1)}",
|
148
|
+
task_type: ['import', 'export', 'backup', 'cleanup', 'report'][i],
|
149
|
+
priority: ['low', 'normal', 'high'][rand(3)]
|
150
|
+
).publish
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
**Consumer:**
|
155
|
+
```ruby
|
156
|
+
# Start processing tasks
|
157
|
+
TaskMessage.subscribe
|
158
|
+
|
159
|
+
puts "🔧 Task processor started. Waiting for tasks..."
|
160
|
+
sleep # Keep running
|
161
|
+
```
|
162
|
+
|
163
|
+
### 2. Multiple Services Communication
|
164
|
+
|
165
|
+
**User Service:**
|
166
|
+
```ruby
|
167
|
+
class UserCreated < SmartMessage::Base
|
168
|
+
transport :redis_queue
|
169
|
+
|
170
|
+
property :user_id, required: true
|
171
|
+
property :name, required: true
|
172
|
+
property :email, required: true
|
173
|
+
|
174
|
+
def process
|
175
|
+
puts "👤 User created: #{name} (#{email})"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Simulate user creation
|
180
|
+
User.after_create do |user|
|
181
|
+
UserCreated.new(
|
182
|
+
user_id: user.id,
|
183
|
+
name: user.name,
|
184
|
+
email: user.email,
|
185
|
+
_sm_header: {
|
186
|
+
from: 'user_service',
|
187
|
+
to: 'notification_service'
|
188
|
+
}
|
189
|
+
).publish
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
**Notification Service:**
|
194
|
+
```ruby
|
195
|
+
# Create transport for pattern subscription
|
196
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
197
|
+
queue_prefix: 'notifications',
|
198
|
+
consumer_group: 'notification_workers'
|
199
|
+
)
|
200
|
+
|
201
|
+
# Subscribe to messages directed to notification service
|
202
|
+
transport.subscribe_pattern("#.*.notification_service") do |message_class, message_data|
|
203
|
+
data = JSON.parse(message_data)
|
204
|
+
|
205
|
+
puts "📧 Notification service received: #{message_class}"
|
206
|
+
puts " User: #{data['name']} (#{data['email']})"
|
207
|
+
|
208
|
+
# Send welcome email
|
209
|
+
WelcomeMailer.send_email(data['email'], data['name'])
|
210
|
+
end
|
211
|
+
|
212
|
+
puts "📧 Notification service started. Waiting for user events..."
|
213
|
+
sleep
|
214
|
+
```
|
215
|
+
|
216
|
+
### 3. Load Balancing Workers
|
217
|
+
|
218
|
+
**Setup Multiple Workers:**
|
219
|
+
```ruby
|
220
|
+
# worker1.rb
|
221
|
+
class ProcessingTask < SmartMessage::Base
|
222
|
+
transport :redis_queue, {
|
223
|
+
consumer_group: 'processing_workers'
|
224
|
+
}
|
225
|
+
|
226
|
+
property :data, required: true
|
227
|
+
|
228
|
+
def process
|
229
|
+
worker_id = Thread.current.object_id.to_s[-4..-1]
|
230
|
+
puts "⚙️ Worker-#{worker_id} processing: #{data}"
|
231
|
+
|
232
|
+
# Simulate work
|
233
|
+
sleep(rand(0.5..2.0))
|
234
|
+
|
235
|
+
puts "✅ Worker-#{worker_id} completed: #{data}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
ProcessingTask.subscribe
|
240
|
+
puts "🔧 Worker 1 started"
|
241
|
+
sleep
|
242
|
+
```
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
# worker2.rb (identical code, different process)
|
246
|
+
# worker3.rb (identical code, different process)
|
247
|
+
```
|
248
|
+
|
249
|
+
**Send Work:**
|
250
|
+
```ruby
|
251
|
+
# Send tasks that will be load balanced
|
252
|
+
20.times do |i|
|
253
|
+
ProcessingTask.new(
|
254
|
+
data: "Task #{i + 1}",
|
255
|
+
_sm_header: {
|
256
|
+
from: 'task_scheduler',
|
257
|
+
to: 'worker_pool'
|
258
|
+
}
|
259
|
+
).publish
|
260
|
+
end
|
261
|
+
|
262
|
+
puts "📤 Sent 20 tasks to worker pool"
|
263
|
+
```
|
264
|
+
|
265
|
+
## Pattern-Based Routing
|
266
|
+
|
267
|
+
### Basic Patterns
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new
|
271
|
+
|
272
|
+
# Messages TO specific service
|
273
|
+
transport.subscribe_pattern("#.*.payment_service") do |msg_class, data|
|
274
|
+
puts "💳 Payment service: #{msg_class}"
|
275
|
+
end
|
276
|
+
|
277
|
+
# Messages FROM specific service
|
278
|
+
transport.subscribe_pattern("#.api_gateway.*") do |msg_class, data|
|
279
|
+
puts "🌐 From API Gateway: #{msg_class}"
|
280
|
+
end
|
281
|
+
|
282
|
+
# Specific message types
|
283
|
+
transport.subscribe_pattern("order.#.*.*") do |msg_class, data|
|
284
|
+
puts "📦 Order message: #{msg_class}"
|
285
|
+
end
|
286
|
+
|
287
|
+
# Broadcast messages
|
288
|
+
transport.subscribe_pattern("#.*.broadcast") do |msg_class, data|
|
289
|
+
puts "📢 Broadcast: #{msg_class}"
|
290
|
+
end
|
291
|
+
```
|
292
|
+
|
293
|
+
### Wildcard Examples
|
294
|
+
|
295
|
+
| Pattern | Matches | Example |
|
296
|
+
|---------|---------|---------|
|
297
|
+
| `#.*.my_service` | All messages TO my_service | `order.ordermessage.api.my_service` |
|
298
|
+
| `#.admin.*` | All messages FROM admin | `user.usercreated.admin.notification` |
|
299
|
+
| `order.#.*.*` | All order messages | `order.ordercreated.api.payment` |
|
300
|
+
| `*.*.*.broadcast` | All broadcasts | `alert.systemalert.monitor.broadcast` |
|
301
|
+
| `#.#.#.urgent` | All urgent messages | `emergency.alert.security.urgent` |
|
302
|
+
|
303
|
+
## Fluent API
|
304
|
+
|
305
|
+
The fluent API provides an expressive way to build subscriptions:
|
306
|
+
|
307
|
+
```ruby
|
308
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new
|
309
|
+
|
310
|
+
# Simple fluent subscriptions
|
311
|
+
transport.where
|
312
|
+
.from('api_service')
|
313
|
+
.subscribe { |msg, data| puts "From API: #{msg}" }
|
314
|
+
|
315
|
+
transport.where
|
316
|
+
.to('my_service')
|
317
|
+
.subscribe { |msg, data| puts "To Me: #{msg}" }
|
318
|
+
|
319
|
+
transport.where
|
320
|
+
.type('OrderMessage')
|
321
|
+
.subscribe { |msg, data| puts "Order: #{msg}" }
|
322
|
+
|
323
|
+
# Combined criteria
|
324
|
+
transport.where
|
325
|
+
.from('web_app')
|
326
|
+
.to('analytics_service')
|
327
|
+
.subscribe { |msg, data| puts "Web → Analytics: #{msg}" }
|
328
|
+
|
329
|
+
# Load balancing
|
330
|
+
transport.where
|
331
|
+
.to('shared_service')
|
332
|
+
.consumer_group('shared_workers')
|
333
|
+
.subscribe { |msg, data| puts "Shared worker: #{msg}" }
|
334
|
+
```
|
335
|
+
|
336
|
+
## Queue Management
|
337
|
+
|
338
|
+
### Monitor Queue Status
|
339
|
+
|
340
|
+
```ruby
|
341
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new
|
342
|
+
|
343
|
+
# Get queue statistics
|
344
|
+
stats = transport.queue_stats
|
345
|
+
puts "📊 Queue Statistics:"
|
346
|
+
stats.each do |queue_name, info|
|
347
|
+
puts " #{queue_name}:"
|
348
|
+
puts " Messages: #{info[:length]}"
|
349
|
+
puts " Consumers: #{info[:consumers]}"
|
350
|
+
puts " Pattern: #{info[:pattern]}"
|
351
|
+
puts ""
|
352
|
+
end
|
353
|
+
|
354
|
+
# Show routing table
|
355
|
+
routing_table = transport.routing_table
|
356
|
+
puts "🗺️ Routing Table:"
|
357
|
+
routing_table.each do |pattern, queues|
|
358
|
+
puts " '#{pattern}' → #{queues.join(', ')}"
|
359
|
+
end
|
360
|
+
```
|
361
|
+
|
362
|
+
### Health Monitoring
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
def monitor_queues(transport)
|
366
|
+
stats = transport.queue_stats
|
367
|
+
|
368
|
+
# Check for problems
|
369
|
+
problems = []
|
370
|
+
|
371
|
+
stats.each do |queue, info|
|
372
|
+
if info[:length] > 100
|
373
|
+
problems << "⚠️ High load: #{queue} has #{info[:length]} messages"
|
374
|
+
elsif info[:length] > 0 && info[:consumers] == 0
|
375
|
+
problems << "🔴 No consumers: #{queue} has #{info[:length]} pending messages"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
if problems.any?
|
380
|
+
puts "Queue Issues:"
|
381
|
+
problems.each { |problem| puts " #{problem}" }
|
382
|
+
else
|
383
|
+
puts "✅ All queues healthy"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Monitor periodically
|
388
|
+
loop do
|
389
|
+
monitor_queues(transport)
|
390
|
+
sleep 30
|
391
|
+
end
|
392
|
+
```
|
393
|
+
|
394
|
+
## Production Configuration
|
395
|
+
|
396
|
+
### Environment-Specific Settings
|
397
|
+
|
398
|
+
```ruby
|
399
|
+
# config/environments/development.rb
|
400
|
+
SmartMessage.configure do |config|
|
401
|
+
config.transport = :redis_queue
|
402
|
+
config.transport_options = {
|
403
|
+
url: 'redis://localhost:6379',
|
404
|
+
db: 15, # Use test database
|
405
|
+
queue_prefix: 'myapp_dev',
|
406
|
+
consumer_group: 'dev_workers',
|
407
|
+
block_time: 1000, # 1 second for quick debugging
|
408
|
+
debug: true
|
409
|
+
}
|
410
|
+
end
|
411
|
+
|
412
|
+
# config/environments/production.rb
|
413
|
+
SmartMessage.configure do |config|
|
414
|
+
config.transport = :redis_queue
|
415
|
+
config.transport_options = {
|
416
|
+
url: ENV['REDIS_URL'] || 'redis://redis.prod.company.com:6379',
|
417
|
+
db: 0,
|
418
|
+
queue_prefix: 'myapp_prod',
|
419
|
+
consumer_group: 'prod_workers',
|
420
|
+
block_time: 5000, # 5 seconds for efficiency
|
421
|
+
max_queue_length: 50000, # Large queues
|
422
|
+
max_retries: 3,
|
423
|
+
dead_letter_queue: true,
|
424
|
+
pool_size: 10 # Connection pooling
|
425
|
+
}
|
426
|
+
end
|
427
|
+
```
|
428
|
+
|
429
|
+
### Worker Configuration
|
430
|
+
|
431
|
+
```ruby
|
432
|
+
# config/workers.rb
|
433
|
+
class ApplicationWorker
|
434
|
+
def self.start
|
435
|
+
# Start multiple worker types
|
436
|
+
start_order_workers(3) # 3 order processing workers
|
437
|
+
start_email_workers(2) # 2 email workers
|
438
|
+
start_report_workers(1) # 1 report worker
|
439
|
+
start_general_workers(5) # 5 general purpose workers
|
440
|
+
end
|
441
|
+
|
442
|
+
def self.start_order_workers(count)
|
443
|
+
count.times do |i|
|
444
|
+
Thread.new do
|
445
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
446
|
+
consumer_group: 'order_workers',
|
447
|
+
consumer_id: "order_worker_#{i + 1}"
|
448
|
+
)
|
449
|
+
|
450
|
+
transport.where
|
451
|
+
.to('order_service')
|
452
|
+
.consumer_group('order_workers')
|
453
|
+
.subscribe { |msg, data| puts "Order Worker #{i + 1}: #{msg}" }
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
# Similar methods for other worker types...
|
459
|
+
end
|
460
|
+
|
461
|
+
# Start all workers
|
462
|
+
ApplicationWorker.start
|
463
|
+
```
|
464
|
+
|
465
|
+
### Health Checks
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
# lib/health_check.rb
|
469
|
+
class RedisQueueHealthCheck
|
470
|
+
def self.check
|
471
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new
|
472
|
+
|
473
|
+
begin
|
474
|
+
# Test connectivity
|
475
|
+
connected = transport.connected?
|
476
|
+
|
477
|
+
# Check queue health
|
478
|
+
stats = transport.queue_stats
|
479
|
+
queue_issues = stats.select { |_, info| info[:length] > 1000 || (info[:length] > 0 && info[:consumers] == 0) }
|
480
|
+
|
481
|
+
{
|
482
|
+
status: connected && queue_issues.empty? ? 'healthy' : 'unhealthy',
|
483
|
+
connected: connected,
|
484
|
+
total_queues: stats.size,
|
485
|
+
total_messages: stats.values.sum { |info| info[:length] },
|
486
|
+
total_consumers: stats.values.sum { |info| info[:consumers] || 0 },
|
487
|
+
queue_issues: queue_issues
|
488
|
+
}
|
489
|
+
rescue => e
|
490
|
+
{
|
491
|
+
status: 'error',
|
492
|
+
error: e.message
|
493
|
+
}
|
494
|
+
ensure
|
495
|
+
transport.disconnect
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# Use in Rails health check endpoint
|
501
|
+
class HealthController < ApplicationController
|
502
|
+
def show
|
503
|
+
health_info = RedisQueueHealthCheck.check
|
504
|
+
|
505
|
+
if health_info[:status] == 'healthy'
|
506
|
+
render json: health_info, status: :ok
|
507
|
+
else
|
508
|
+
render json: health_info, status: :service_unavailable
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
```
|
513
|
+
|
514
|
+
## Common Patterns and Best Practices
|
515
|
+
|
516
|
+
### 1. Request-Response Pattern
|
517
|
+
|
518
|
+
```ruby
|
519
|
+
# Request message
|
520
|
+
class ProcessingRequest < SmartMessage::Base
|
521
|
+
transport :redis_queue
|
522
|
+
|
523
|
+
property :request_id, required: true
|
524
|
+
property :data, required: true
|
525
|
+
property :callback_service, required: true
|
526
|
+
|
527
|
+
def process
|
528
|
+
# Process the request
|
529
|
+
result = process_data(data)
|
530
|
+
|
531
|
+
# Send response back
|
532
|
+
ProcessingResponse.new(
|
533
|
+
request_id: request_id,
|
534
|
+
result: result,
|
535
|
+
status: 'success',
|
536
|
+
_sm_header: {
|
537
|
+
from: 'processing_service',
|
538
|
+
to: callback_service
|
539
|
+
}
|
540
|
+
).publish
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
# Response message
|
545
|
+
class ProcessingResponse < SmartMessage::Base
|
546
|
+
transport :redis_queue
|
547
|
+
|
548
|
+
property :request_id, required: true
|
549
|
+
property :result, required: true
|
550
|
+
property :status, required: true
|
551
|
+
|
552
|
+
def process
|
553
|
+
puts "📥 Response for request #{request_id}: #{status}"
|
554
|
+
# Handle response
|
555
|
+
end
|
556
|
+
end
|
557
|
+
```
|
558
|
+
|
559
|
+
### 2. Saga Pattern
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
class OrderSaga
|
563
|
+
def self.start_order(order_data)
|
564
|
+
# Step 1: Reserve inventory
|
565
|
+
ReserveInventory.new(
|
566
|
+
saga_id: order_data[:saga_id],
|
567
|
+
order_id: order_data[:order_id],
|
568
|
+
items: order_data[:items],
|
569
|
+
_sm_header: { from: 'order_saga', to: 'inventory_service' }
|
570
|
+
).publish
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
class ReserveInventory < SmartMessage::Base
|
575
|
+
transport :redis_queue
|
576
|
+
|
577
|
+
def process
|
578
|
+
if inventory_available?
|
579
|
+
# Success - continue saga
|
580
|
+
ProcessPayment.new(
|
581
|
+
saga_id: saga_id,
|
582
|
+
order_id: order_id,
|
583
|
+
amount: calculate_amount,
|
584
|
+
_sm_header: { from: 'inventory_service', to: 'payment_service' }
|
585
|
+
).publish
|
586
|
+
else
|
587
|
+
# Failure - compensate
|
588
|
+
OrderFailed.new(
|
589
|
+
saga_id: saga_id,
|
590
|
+
reason: 'Inventory not available',
|
591
|
+
_sm_header: { from: 'inventory_service', to: 'order_saga' }
|
592
|
+
).publish
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
```
|
597
|
+
|
598
|
+
### 3. Event Sourcing
|
599
|
+
|
600
|
+
```ruby
|
601
|
+
class EventStore
|
602
|
+
def self.append_event(event)
|
603
|
+
EventAppended.new(
|
604
|
+
event_id: SecureRandom.uuid,
|
605
|
+
event_type: event.class.name,
|
606
|
+
event_data: event.to_h,
|
607
|
+
timestamp: Time.now,
|
608
|
+
_sm_header: { from: 'event_store', to: 'broadcast' }
|
609
|
+
).publish
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
# Projections subscribe to events
|
614
|
+
class OrderProjection < SmartMessage::Base
|
615
|
+
transport :redis_queue
|
616
|
+
|
617
|
+
def self.subscribe_to_events
|
618
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new
|
619
|
+
|
620
|
+
transport.subscribe_pattern("event.#.*.*") do |msg_class, data|
|
621
|
+
event = JSON.parse(data)
|
622
|
+
|
623
|
+
case event['event_type']
|
624
|
+
when 'OrderCreated'
|
625
|
+
update_order_projection(event['event_data'])
|
626
|
+
when 'OrderCancelled'
|
627
|
+
cancel_order_projection(event['event_data'])
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
```
|
633
|
+
|
634
|
+
## Troubleshooting
|
635
|
+
|
636
|
+
### Debug Mode
|
637
|
+
|
638
|
+
```ruby
|
639
|
+
# Enable detailed logging
|
640
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
641
|
+
debug: true,
|
642
|
+
log_level: :debug
|
643
|
+
)
|
644
|
+
|
645
|
+
# Or set environment variable
|
646
|
+
ENV['SMART_MESSAGE_DEBUG'] = 'true'
|
647
|
+
```
|
648
|
+
|
649
|
+
### Common Issues
|
650
|
+
|
651
|
+
**Messages not being processed:**
|
652
|
+
```ruby
|
653
|
+
# Check if anyone is subscribed
|
654
|
+
stats = transport.queue_stats
|
655
|
+
stats.each do |queue, info|
|
656
|
+
if info[:length] > 0 && info[:consumers] == 0
|
657
|
+
puts "No consumers for #{queue}"
|
658
|
+
end
|
659
|
+
end
|
660
|
+
```
|
661
|
+
|
662
|
+
**Pattern not matching:**
|
663
|
+
```ruby
|
664
|
+
# Test pattern matching
|
665
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new
|
666
|
+
pattern = "#.*.my_service"
|
667
|
+
test_key = "order.ordermessage.api.my_service"
|
668
|
+
|
669
|
+
# This uses private method for testing
|
670
|
+
matches = transport.send(:routing_key_matches_pattern?, test_key, pattern)
|
671
|
+
puts "Pattern matches: #{matches}"
|
672
|
+
```
|
673
|
+
|
674
|
+
**High memory usage:**
|
675
|
+
```ruby
|
676
|
+
# Check for large queues
|
677
|
+
stats = transport.queue_stats
|
678
|
+
large_queues = stats.select { |_, info| info[:length] > 1000 }
|
679
|
+
puts "Large queues: #{large_queues}"
|
680
|
+
|
681
|
+
# Configure queue limits
|
682
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
683
|
+
max_queue_length: 5000
|
684
|
+
)
|
685
|
+
```
|
686
|
+
|
687
|
+
## Next Steps
|
688
|
+
|
689
|
+
Now that you understand the basics, explore these advanced topics:
|
690
|
+
|
691
|
+
1. **[Advanced Routing Patterns](redis-queue-patterns.md)** - Complex routing scenarios
|
692
|
+
2. **[Production Deployment](redis-queue-production.md)** - Production-ready configurations
|
693
|
+
3. **[Complete Transport Reference](../transports/redis-queue-transport.md)** - Full API documentation
|
694
|
+
|
695
|
+
Or dive into the [complete examples](https://github.com/madbomber/smart_message/tree/main/examples/redis_queue) to see real-world usage patterns.
|
696
|
+
|
697
|
+
The Redis Queue Transport provides the perfect balance of performance, reliability, and intelligent routing for modern Ruby applications. Start with these patterns and gradually incorporate more advanced features as your needs grow.
|