bunny_farm 0.1.2
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 +7 -0
- data/.envrc +1 -0
- data/.github/workflows/docs.yml +38 -0
- data/.gitignore +11 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +61 -0
- data/COMMITS.md +196 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +330 -0
- data/Rakefile +9 -0
- data/bunny_farm.gemspec +30 -0
- data/config/bunny.yml.erb +29 -0
- data/config/bunny_test.yml.erb +29 -0
- data/config/hipchat.yml.erb +12 -0
- data/docs/api/configuration.md +9 -0
- data/docs/api/consumer.md +8 -0
- data/docs/api/message-class.md +419 -0
- data/docs/api/publisher.md +9 -0
- data/docs/architecture/integration.md +8 -0
- data/docs/architecture/message-flow.md +11 -0
- data/docs/architecture/overview.md +448 -0
- data/docs/architecture/scaling.md +8 -0
- data/docs/assets/actions_dsl_flow.svg +109 -0
- data/docs/assets/architecture_overview.svg +152 -0
- data/docs/assets/best_practices_patterns.svg +203 -0
- data/docs/assets/bunny_farm_logo.png +0 -0
- data/docs/assets/configuration_api_methods.svg +104 -0
- data/docs/assets/configuration_flow.svg +130 -0
- data/docs/assets/configuration_hierarchy.svg +70 -0
- data/docs/assets/data_processing_pipeline.svg +131 -0
- data/docs/assets/debugging_monitoring.svg +165 -0
- data/docs/assets/ecommerce_example_flow.svg +145 -0
- data/docs/assets/email_campaign_example.svg +127 -0
- data/docs/assets/environment_variables_map.svg +78 -0
- data/docs/assets/error_handling_flow.svg +114 -0
- data/docs/assets/favicon.ico +1 -0
- data/docs/assets/fields_dsl_structure.svg +89 -0
- data/docs/assets/instance_methods_lifecycle.svg +137 -0
- data/docs/assets/integration_patterns.svg +207 -0
- data/docs/assets/json_serialization_flow.svg +153 -0
- data/docs/assets/logo.svg +4 -0
- data/docs/assets/message_api_overview.svg +126 -0
- data/docs/assets/message_encapsulation.svg +113 -0
- data/docs/assets/message_lifecycle.svg +110 -0
- data/docs/assets/message_structure.svg +138 -0
- data/docs/assets/publisher_consumer_api.svg +120 -0
- data/docs/assets/scaling_deployment_patterns.svg +195 -0
- data/docs/assets/smart_routing_diagram.svg +131 -0
- data/docs/assets/system_architecture_overview.svg +155 -0
- data/docs/assets/task_scheduling_flow.svg +139 -0
- data/docs/assets/testing_strategies.svg +146 -0
- data/docs/assets/workflow_patterns.svg +183 -0
- data/docs/assets/yaml_config_structure.svg +72 -0
- data/docs/configuration/environment-variables.md +14 -0
- data/docs/configuration/overview.md +373 -0
- data/docs/configuration/programmatic-setup.md +10 -0
- data/docs/configuration/yaml-configuration.md +12 -0
- data/docs/core-features/configuration.md +528 -0
- data/docs/core-features/error-handling.md +82 -0
- data/docs/core-features/json-serialization.md +545 -0
- data/docs/core-features/message-design.md +406 -0
- data/docs/core-features/smart-routing.md +467 -0
- data/docs/core-features/task-scheduling.md +67 -0
- data/docs/core-features/workflow-support.md +112 -0
- data/docs/development/contributing.md +345 -0
- data/docs/development/roadmap.md +9 -0
- data/docs/development/testing.md +14 -0
- data/docs/examples/order-processing.md +10 -0
- data/docs/examples/overview.md +269 -0
- data/docs/examples/real-world.md +8 -0
- data/docs/examples/simple-producer-consumer.md +15 -0
- data/docs/examples/task-scheduler.md +9 -0
- data/docs/getting-started/basic-concepts.md +274 -0
- data/docs/getting-started/installation.md +122 -0
- data/docs/getting-started/quick-start.md +158 -0
- data/docs/index.md +106 -0
- data/docs/message-structure/actions-dsl.md +163 -0
- data/docs/message-structure/fields-dsl.md +146 -0
- data/docs/message-structure/instance-methods.md +115 -0
- data/docs/message-structure/overview.md +211 -0
- data/examples/README.md +212 -0
- data/examples/consumer.rb +41 -0
- data/examples/images/message_flow.svg +87 -0
- data/examples/images/order_workflow.svg +122 -0
- data/examples/images/producer_consumer.svg +96 -0
- data/examples/images/task_scheduler.svg +140 -0
- data/examples/order_processor.rb +238 -0
- data/examples/producer.rb +60 -0
- data/examples/simple_message.rb +43 -0
- data/examples/task_scheduler.rb +263 -0
- data/images/architecture_overview.svg +152 -0
- data/images/bunny_farm_logo.png +0 -0
- data/images/configuration_flow.svg +130 -0
- data/images/message_structure.svg +138 -0
- data/lib/bunny_farm/.irbrc +7 -0
- data/lib/bunny_farm/generic_consumer.rb +12 -0
- data/lib/bunny_farm/hash_ext.rb +37 -0
- data/lib/bunny_farm/init_bunny.rb +137 -0
- data/lib/bunny_farm/init_hipchat.rb +49 -0
- data/lib/bunny_farm/message.rb +218 -0
- data/lib/bunny_farm/message_elements.rb +25 -0
- data/lib/bunny_farm/version.rb +3 -0
- data/lib/bunny_farm.rb +9 -0
- data/mkdocs.yml +148 -0
- metadata +244 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# Smart Routing
|
|
2
|
+
|
|
3
|
+
BunnyFarm's smart routing system automatically creates predictable routing keys based on your message classes and actions. This eliminates the need for manual routing configuration while maintaining full transparency and debuggability.
|
|
4
|
+
|
|
5
|
+
## How Smart Routing Works
|
|
6
|
+
|
|
7
|
+
### Routing Key Pattern
|
|
8
|
+
|
|
9
|
+
BunnyFarm uses a simple, predictable pattern for routing keys:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
MessageClassName.action
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Examples:**
|
|
16
|
+
- `OrderMessage.process`
|
|
17
|
+
- `EmailMessage.send`
|
|
18
|
+
- `ReportMessage.generate`
|
|
19
|
+
- `UserRegistrationMessage.create_account`
|
|
20
|
+
|
|
21
|
+
### Automatic Generation
|
|
22
|
+
|
|
23
|
+
When you publish a message, the routing key is automatically generated:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
class OrderMessage < BunnyFarm::Message
|
|
27
|
+
actions :validate, :process, :ship
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Publishing automatically creates routing keys
|
|
31
|
+
message = OrderMessage.new
|
|
32
|
+
message.publish('validate') # Routing key: OrderMessage.validate
|
|
33
|
+
message.publish('process') # Routing key: OrderMessage.process
|
|
34
|
+
message.publish('ship') # Routing key: OrderMessage.ship
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Routing Flow
|
|
38
|
+
|
|
39
|
+
### 1. Publisher Side
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
# 1. Create message
|
|
43
|
+
order_msg = OrderMessage.new
|
|
44
|
+
order_msg[:order_id] = 12345
|
|
45
|
+
|
|
46
|
+
# 2. Publish with action
|
|
47
|
+
order_msg.publish('process')
|
|
48
|
+
|
|
49
|
+
# 3. BunnyFarm generates routing key: "OrderMessage.process"
|
|
50
|
+
# 4. Message sent to RabbitMQ with this routing key
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. RabbitMQ Routing
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Publisher → Exchange → Queue(s) → Consumer(s)
|
|
57
|
+
↑
|
|
58
|
+
Routes based on
|
|
59
|
+
"OrderMessage.process"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Consumer Side
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# 1. Consumer receives message with routing key "OrderMessage.process"
|
|
66
|
+
# 2. BunnyFarm parses routing key → class: OrderMessage, action: process
|
|
67
|
+
# 3. Message deserialized to OrderMessage instance
|
|
68
|
+
# 4. The 'process' method is called automatically
|
|
69
|
+
|
|
70
|
+
class OrderMessage < BunnyFarm::Message
|
|
71
|
+
def process
|
|
72
|
+
# This method is called automatically
|
|
73
|
+
puts "Processing order #{@items[:order_id]}"
|
|
74
|
+
success!
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Exchange and Queue Configuration
|
|
80
|
+
|
|
81
|
+
### Topic Exchange
|
|
82
|
+
|
|
83
|
+
BunnyFarm typically uses RabbitMQ's **topic exchange** for flexible routing:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
BunnyFarm.config do
|
|
87
|
+
exchange_name 'bunny_farm_exchange'
|
|
88
|
+
exchange_type :topic # Supports pattern matching
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Queue Bindings
|
|
93
|
+
|
|
94
|
+
Queues can bind to specific routing patterns:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# Bind to all OrderMessage actions
|
|
98
|
+
queue.bind(exchange, routing_key: 'OrderMessage.*')
|
|
99
|
+
|
|
100
|
+
# Bind to specific actions only
|
|
101
|
+
queue.bind(exchange, routing_key: 'OrderMessage.process')
|
|
102
|
+
|
|
103
|
+
# Bind to all messages
|
|
104
|
+
queue.bind(exchange, routing_key: '#')
|
|
105
|
+
|
|
106
|
+
# Bind to all validation actions across message types
|
|
107
|
+
queue.bind(exchange, routing_key: '*.validate')
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Routing Patterns
|
|
111
|
+
|
|
112
|
+
### 1. Single Message Type, Single Action
|
|
113
|
+
|
|
114
|
+
**Use case:** Dedicated worker for specific operations
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# Worker specializes in order processing
|
|
118
|
+
BunnyFarm.config do
|
|
119
|
+
queue_name 'order_processing'
|
|
120
|
+
routing_key 'OrderMessage.process'
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Only processes OrderMessage.process
|
|
124
|
+
BunnyFarm.manage
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 2. Single Message Type, Multiple Actions
|
|
128
|
+
|
|
129
|
+
**Use case:** Worker handles all operations for one domain
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
# Worker handles all order operations
|
|
133
|
+
BunnyFarm.config do
|
|
134
|
+
queue_name 'order_worker'
|
|
135
|
+
routing_key 'OrderMessage.*' # All OrderMessage actions
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class OrderMessage < BunnyFarm::Message
|
|
139
|
+
actions :validate, :process, :ship, :cancel
|
|
140
|
+
|
|
141
|
+
def validate; end # Handled by order_worker
|
|
142
|
+
def process; end # Handled by order_worker
|
|
143
|
+
def ship; end # Handled by order_worker
|
|
144
|
+
def cancel; end # Handled by order_worker
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 3. Multiple Message Types, Specific Actions
|
|
149
|
+
|
|
150
|
+
**Use case:** Worker specializes in one type of operation across domains
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
# Worker handles validation for all message types
|
|
154
|
+
BunnyFarm.config do
|
|
155
|
+
queue_name 'validation_worker'
|
|
156
|
+
routing_key '*.validate' # All validation actions
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# These would all be handled by validation_worker:
|
|
160
|
+
# OrderMessage.validate
|
|
161
|
+
# CustomerMessage.validate
|
|
162
|
+
# ProductMessage.validate
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. Multiple Message Types, All Actions
|
|
166
|
+
|
|
167
|
+
**Use case:** General-purpose worker
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# Worker handles everything
|
|
171
|
+
BunnyFarm.config do
|
|
172
|
+
queue_name 'general_worker'
|
|
173
|
+
routing_key '#' # All messages
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Advanced Routing Scenarios
|
|
178
|
+
|
|
179
|
+
### Priority Queues
|
|
180
|
+
|
|
181
|
+
Route urgent messages to high-priority queues:
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
class UrgentOrderMessage < BunnyFarm::Message
|
|
185
|
+
actions :process_immediately
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# High-priority worker
|
|
189
|
+
BunnyFarm.config do
|
|
190
|
+
queue_name 'urgent_orders'
|
|
191
|
+
routing_key 'UrgentOrderMessage.*'
|
|
192
|
+
queue_options do
|
|
193
|
+
arguments 'x-max-priority' => 10
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Regular priority worker
|
|
198
|
+
BunnyFarm.config do
|
|
199
|
+
queue_name 'regular_orders'
|
|
200
|
+
routing_key 'OrderMessage.*'
|
|
201
|
+
end
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Geographic Routing
|
|
205
|
+
|
|
206
|
+
Route messages based on regions:
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
class USOrderMessage < BunnyFarm::Message
|
|
210
|
+
actions :process, :ship
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
class EUOrderMessage < BunnyFarm::Message
|
|
214
|
+
actions :process, :ship
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# US worker
|
|
218
|
+
BunnyFarm.config do
|
|
219
|
+
queue_name 'us_orders'
|
|
220
|
+
routing_key 'USOrderMessage.*'
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# EU worker
|
|
224
|
+
BunnyFarm.config do
|
|
225
|
+
queue_name 'eu_orders'
|
|
226
|
+
routing_key 'EUOrderMessage.*'
|
|
227
|
+
end
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Load Balancing
|
|
231
|
+
|
|
232
|
+
Multiple workers can consume from the same queue:
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# Worker 1
|
|
236
|
+
BunnyFarm.config do
|
|
237
|
+
app_id 'worker_1'
|
|
238
|
+
queue_name 'order_processing'
|
|
239
|
+
routing_key 'OrderMessage.process'
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Worker 2
|
|
243
|
+
BunnyFarm.config do
|
|
244
|
+
app_id 'worker_2'
|
|
245
|
+
queue_name 'order_processing' # Same queue
|
|
246
|
+
routing_key 'OrderMessage.process'
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# RabbitMQ automatically load balances between workers
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Routing Key Introspection
|
|
253
|
+
|
|
254
|
+
### Debugging Routing
|
|
255
|
+
|
|
256
|
+
Check what routing key will be generated:
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
class OrderMessage < BunnyFarm::Message
|
|
260
|
+
actions :process
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
message = OrderMessage.new
|
|
264
|
+
routing_key = "#{message.class.name}.process"
|
|
265
|
+
puts routing_key # => "OrderMessage.process"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Runtime Routing Information
|
|
269
|
+
|
|
270
|
+
Access routing information during processing:
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
class OrderMessage < BunnyFarm::Message
|
|
274
|
+
def process
|
|
275
|
+
puts "Processing with routing key: #{self.class.name}.process"
|
|
276
|
+
puts "Message class: #{self.class.name}"
|
|
277
|
+
puts "Action: process"
|
|
278
|
+
|
|
279
|
+
success!
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Error Routing
|
|
285
|
+
|
|
286
|
+
### Dead Letter Queues
|
|
287
|
+
|
|
288
|
+
Failed messages can be routed to error queues:
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
BunnyFarm.config do
|
|
292
|
+
queue_name 'order_processing'
|
|
293
|
+
routing_key 'OrderMessage.*'
|
|
294
|
+
|
|
295
|
+
queue_options do
|
|
296
|
+
arguments({
|
|
297
|
+
'x-dead-letter-exchange' => 'failed_messages',
|
|
298
|
+
'x-dead-letter-routing-key' => 'failed.OrderMessage'
|
|
299
|
+
})
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Retry Queues
|
|
305
|
+
|
|
306
|
+
Implement retry logic with delayed routing:
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
class OrderMessage < BunnyFarm::Message
|
|
310
|
+
def process
|
|
311
|
+
begin
|
|
312
|
+
perform_processing
|
|
313
|
+
success!
|
|
314
|
+
rescue RetryableError => e
|
|
315
|
+
if retry_count < 3
|
|
316
|
+
# Publish to retry queue with delay
|
|
317
|
+
retry_message = self.class.new(@items)
|
|
318
|
+
retry_message[:retry_count] = retry_count + 1
|
|
319
|
+
retry_message.publish_delayed('process', delay: 30.seconds)
|
|
320
|
+
success! # Don't NACK original message
|
|
321
|
+
else
|
|
322
|
+
failure("Max retries exceeded: #{e.message}")
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
successful?
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
private
|
|
330
|
+
|
|
331
|
+
def retry_count
|
|
332
|
+
@items[:retry_count] || 0
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Monitoring and Debugging
|
|
338
|
+
|
|
339
|
+
### RabbitMQ Management UI
|
|
340
|
+
|
|
341
|
+
Use RabbitMQ's management interface to monitor routing:
|
|
342
|
+
|
|
343
|
+
1. **Exchanges tab** - See message routing statistics
|
|
344
|
+
2. **Queues tab** - Monitor queue depths and consumption rates
|
|
345
|
+
3. **Connections tab** - View active publishers and consumers
|
|
346
|
+
|
|
347
|
+
### Routing Metrics
|
|
348
|
+
|
|
349
|
+
Track routing patterns in your application:
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
class OrderMessage < BunnyFarm::Message
|
|
353
|
+
def process
|
|
354
|
+
# Track routing metrics
|
|
355
|
+
Metrics.increment("message.routed.#{self.class.name}.process")
|
|
356
|
+
|
|
357
|
+
# Your processing logic
|
|
358
|
+
perform_order_processing
|
|
359
|
+
|
|
360
|
+
Metrics.increment("message.processed.#{self.class.name}.process")
|
|
361
|
+
success!
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Logging Routing Events
|
|
367
|
+
|
|
368
|
+
Log routing information for debugging:
|
|
369
|
+
|
|
370
|
+
```ruby
|
|
371
|
+
class OrderMessage < BunnyFarm::Message
|
|
372
|
+
def process
|
|
373
|
+
logger.info "Processing message",
|
|
374
|
+
routing_key: "#{self.class.name}.process",
|
|
375
|
+
message_id: @items[:order_id]
|
|
376
|
+
|
|
377
|
+
# Processing logic
|
|
378
|
+
success!
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Best Practices
|
|
384
|
+
|
|
385
|
+
### 1. Predictable Naming
|
|
386
|
+
|
|
387
|
+
Use clear, consistent class and action names:
|
|
388
|
+
|
|
389
|
+
```ruby
|
|
390
|
+
# Good: Clear domain and action
|
|
391
|
+
class CustomerRegistrationMessage < BunnyFarm::Message
|
|
392
|
+
actions :validate_email, :create_account, :send_welcome
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Avoid: Vague or abbreviated names
|
|
396
|
+
class CRM < BunnyFarm::Message
|
|
397
|
+
actions :val, :cr8, :snd
|
|
398
|
+
end
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### 2. Logical Grouping
|
|
402
|
+
|
|
403
|
+
Group related actions in the same message class:
|
|
404
|
+
|
|
405
|
+
```ruby
|
|
406
|
+
# Good: Related order operations
|
|
407
|
+
class OrderMessage < BunnyFarm::Message
|
|
408
|
+
actions :validate, :process_payment, :fulfill, :ship, :complete
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Avoid: Mixing unrelated operations
|
|
412
|
+
class MixedMessage < BunnyFarm::Message
|
|
413
|
+
actions :process_order, :send_email, :backup_database, :clean_temp_files
|
|
414
|
+
end
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### 3. Queue Design
|
|
418
|
+
|
|
419
|
+
Design queues around processing capabilities:
|
|
420
|
+
|
|
421
|
+
```ruby
|
|
422
|
+
# Good: Separate concerns
|
|
423
|
+
BunnyFarm.config do
|
|
424
|
+
case worker_type
|
|
425
|
+
when 'payment_processor'
|
|
426
|
+
routing_key '*.process_payment'
|
|
427
|
+
when 'email_sender'
|
|
428
|
+
routing_key '*.send_email'
|
|
429
|
+
when 'order_validator'
|
|
430
|
+
routing_key 'OrderMessage.validate'
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### 4. Error Handling
|
|
436
|
+
|
|
437
|
+
Plan for routing errors:
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
def process
|
|
441
|
+
validate_message_structure
|
|
442
|
+
return unless successful?
|
|
443
|
+
|
|
444
|
+
perform_business_logic
|
|
445
|
+
return unless successful?
|
|
446
|
+
|
|
447
|
+
log_success
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
private
|
|
451
|
+
|
|
452
|
+
def validate_message_structure
|
|
453
|
+
required_fields = [:order_id, :customer_id, :amount]
|
|
454
|
+
missing = required_fields.select { |field| @items[field].nil? }
|
|
455
|
+
|
|
456
|
+
failure("Missing required fields: #{missing.join(', ')}") if missing.any?
|
|
457
|
+
end
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Next Steps
|
|
461
|
+
|
|
462
|
+
Understanding smart routing enables you to:
|
|
463
|
+
|
|
464
|
+
- **[Configure](../configuration/overview.md)** routing for your specific needs
|
|
465
|
+
- **[Design message structures](../message-structure/overview.md)** that route effectively
|
|
466
|
+
- **[Scale your architecture](../architecture/scaling.md)** with proper queue design
|
|
467
|
+
- **[Handle errors](error-handling.md)** with appropriate routing strategies
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Task Scheduling
|
|
2
|
+
|
|
3
|
+
BunnyFarm supports delayed message processing and task scheduling, allowing you to build sophisticated time-based workflows and recurring operations.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Delayed Messages
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
class ScheduledTask < BunnyFarm::Message
|
|
11
|
+
actions :schedule, :execute
|
|
12
|
+
|
|
13
|
+
def schedule
|
|
14
|
+
# Schedule execution for later
|
|
15
|
+
delay_seconds = @items[:delay] || 3600 # 1 hour default
|
|
16
|
+
|
|
17
|
+
# Use RabbitMQ delayed message plugin or custom delay queue
|
|
18
|
+
publish_delayed('execute', delay: delay_seconds)
|
|
19
|
+
success!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def execute
|
|
23
|
+
puts "Executing scheduled task at #{Time.current}"
|
|
24
|
+
perform_scheduled_work
|
|
25
|
+
success!
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Recurring Tasks
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
def execute_recurring
|
|
34
|
+
perform_work
|
|
35
|
+
|
|
36
|
+
# Schedule next execution
|
|
37
|
+
next_run = @items[:interval] || 3600 # 1 hour
|
|
38
|
+
self.class.new(@items).publish_delayed('execute_recurring', delay: next_run)
|
|
39
|
+
|
|
40
|
+
success!
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Retry Patterns
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
def execute_with_retry
|
|
48
|
+
begin
|
|
49
|
+
risky_operation
|
|
50
|
+
success!
|
|
51
|
+
rescue => e
|
|
52
|
+
retry_count = (@items[:retry_count] || 0) + 1
|
|
53
|
+
|
|
54
|
+
if retry_count < 3
|
|
55
|
+
delay = 2 ** retry_count # Exponential backoff
|
|
56
|
+
|
|
57
|
+
retry_msg = self.class.new(@items)
|
|
58
|
+
retry_msg[:retry_count] = retry_count
|
|
59
|
+
retry_msg.publish_delayed('execute_with_retry', delay: delay)
|
|
60
|
+
|
|
61
|
+
success!
|
|
62
|
+
else
|
|
63
|
+
failure("Max retries exceeded: #{e.message}")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Workflow Support
|
|
2
|
+
|
|
3
|
+
BunnyFarm excels at building complex, multi-step workflows where messages can trigger subsequent operations, creating sophisticated business processes through message chaining and coordination.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Workflow Patterns
|
|
8
|
+
|
|
9
|
+
### Sequential Workflows
|
|
10
|
+
|
|
11
|
+
Messages can trigger follow-up messages in sequence:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
class OrderWorkflow < BunnyFarm::Message
|
|
15
|
+
actions :start_order, :validate, :process_payment, :fulfill, :ship, :complete
|
|
16
|
+
|
|
17
|
+
def start_order
|
|
18
|
+
# Step 1: Validate order
|
|
19
|
+
self.class.new(@items).publish('validate')
|
|
20
|
+
success!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate
|
|
24
|
+
validate_order_data
|
|
25
|
+
return unless successful?
|
|
26
|
+
|
|
27
|
+
# Step 2: Process payment
|
|
28
|
+
self.class.new(@items).publish('process_payment')
|
|
29
|
+
success!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def process_payment
|
|
33
|
+
charge_customer
|
|
34
|
+
return unless successful?
|
|
35
|
+
|
|
36
|
+
# Step 3: Fulfill order
|
|
37
|
+
self.class.new(@items).publish('fulfill')
|
|
38
|
+
success!
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Conditional Workflows
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
def process_order
|
|
47
|
+
validate_order
|
|
48
|
+
return unless successful?
|
|
49
|
+
|
|
50
|
+
if @items[:requires_approval]
|
|
51
|
+
self.class.new(@items).publish('request_approval')
|
|
52
|
+
else
|
|
53
|
+
self.class.new(@items).publish('auto_process')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
success!
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Parallel Workflows
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
def start_parallel_processing
|
|
64
|
+
# Trigger multiple parallel operations
|
|
65
|
+
InventoryMessage.new(@items).publish('reserve_items')
|
|
66
|
+
PaymentMessage.new(@items).publish('authorize_payment')
|
|
67
|
+
ShippingMessage.new(@items).publish('calculate_shipping')
|
|
68
|
+
|
|
69
|
+
success!
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## State Management
|
|
74
|
+
|
|
75
|
+
### Workflow State Tracking
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
class OrderWorkflow < BunnyFarm::Message
|
|
79
|
+
def initialize
|
|
80
|
+
super
|
|
81
|
+
@items[:workflow_state] = 'initialized'
|
|
82
|
+
@items[:completed_steps] = []
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate
|
|
86
|
+
@items[:workflow_state] = 'validating'
|
|
87
|
+
|
|
88
|
+
perform_validation
|
|
89
|
+
|
|
90
|
+
if successful?
|
|
91
|
+
@items[:completed_steps] << 'validate'
|
|
92
|
+
@items[:workflow_state] = 'validated'
|
|
93
|
+
trigger_next_step
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def trigger_next_step
|
|
100
|
+
case @items[:workflow_state]
|
|
101
|
+
when 'validated'
|
|
102
|
+
self.class.new(@items).publish('process_payment')
|
|
103
|
+
when 'payment_processed'
|
|
104
|
+
self.class.new(@items).publish('fulfill')
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Next Steps
|
|
111
|
+
|
|
112
|
+
Workflows enable complex business processes through message orchestration.
|