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,528 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
BunnyFarm provides flexible configuration options that adapt to different environments and deployment scenarios. This page covers configuration as a core feature that enables the framework's adaptability.
|
|
4
|
+
|
|
5
|
+
## Configuration Philosophy
|
|
6
|
+
|
|
7
|
+
BunnyFarm follows the principle of **sensible defaults with easy customization**:
|
|
8
|
+
|
|
9
|
+
- **Zero configuration** - Works out of the box for development
|
|
10
|
+
- **Environment aware** - Adapts to different deployment contexts
|
|
11
|
+
- **Override hierarchy** - Multiple configuration layers with clear precedence
|
|
12
|
+
- **Runtime flexibility** - Can be configured programmatically or declaratively
|
|
13
|
+
|
|
14
|
+
## Configuration as a Feature
|
|
15
|
+
|
|
16
|
+
### Environment Adaptation
|
|
17
|
+
|
|
18
|
+
BunnyFarm automatically adapts to different environments:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Development - uses localhost defaults
|
|
22
|
+
BunnyFarm.config { env 'development' }
|
|
23
|
+
|
|
24
|
+
# Production - uses environment variables or config files
|
|
25
|
+
BunnyFarm.config do
|
|
26
|
+
env 'production'
|
|
27
|
+
bunny_file 'config/production.yml'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Testing - isolated configuration
|
|
31
|
+
BunnyFarm.config do
|
|
32
|
+
env 'test'
|
|
33
|
+
exchange_name 'test_exchange'
|
|
34
|
+
queue_name 'test_queue'
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Dynamic Configuration
|
|
39
|
+
|
|
40
|
+
Configuration can adapt to runtime conditions:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
BunnyFarm.config do
|
|
44
|
+
# Dynamic host selection
|
|
45
|
+
host case ENV['REGION']
|
|
46
|
+
when 'us-east' then 'amqp-us-east.example.com'
|
|
47
|
+
when 'eu-west' then 'amqp-eu-west.example.com'
|
|
48
|
+
else 'localhost'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Scale queue count based on load
|
|
52
|
+
queue_name "orders_#{ENV['INSTANCE_ID'] || 'default'}"
|
|
53
|
+
|
|
54
|
+
# Conditional features
|
|
55
|
+
if ENV['ENABLE_MONITORING'] == 'true'
|
|
56
|
+
monitoring_enabled true
|
|
57
|
+
metrics_endpoint 'http://prometheus:9090'
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration Methods
|
|
63
|
+
|
|
64
|
+
### 1. Environment Variables
|
|
65
|
+
|
|
66
|
+
The most common production configuration method:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Connection settings
|
|
70
|
+
export AMQP_HOST=amqp.example.com
|
|
71
|
+
export AMQP_PORT=5672
|
|
72
|
+
export AMQP_USER=app_user
|
|
73
|
+
export AMQP_PASS=secret_password
|
|
74
|
+
export AMQP_VHOST=production
|
|
75
|
+
|
|
76
|
+
# Application settings
|
|
77
|
+
export AMQP_EXCHANGE=production_exchange
|
|
78
|
+
export AMQP_QUEUE=production_queue
|
|
79
|
+
export AMQP_ROUTING_KEY='#'
|
|
80
|
+
export AMQP_APP_NAME=order_processor
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Usage:**
|
|
84
|
+
```ruby
|
|
85
|
+
# Automatically uses environment variables
|
|
86
|
+
BunnyFarm.config
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. YAML Configuration Files
|
|
90
|
+
|
|
91
|
+
Structured configuration with ERB templating:
|
|
92
|
+
|
|
93
|
+
**config/bunny.yml.erb:**
|
|
94
|
+
```yaml
|
|
95
|
+
defaults: &defaults
|
|
96
|
+
host: <%= ENV['AMQP_HOST'] || 'localhost' %>
|
|
97
|
+
port: <%= ENV['AMQP_PORT'] || 5672 %>
|
|
98
|
+
user: <%= ENV['AMQP_USER'] || 'guest' %>
|
|
99
|
+
pass: <%= ENV['AMQP_PASS'] || 'guest' %>
|
|
100
|
+
vhost: <%= ENV['AMQP_VHOST'] || '/' %>
|
|
101
|
+
|
|
102
|
+
development:
|
|
103
|
+
<<: *defaults
|
|
104
|
+
exchange_name: dev_exchange
|
|
105
|
+
queue_name: dev_queue
|
|
106
|
+
|
|
107
|
+
staging:
|
|
108
|
+
<<: *defaults
|
|
109
|
+
host: staging-amqp.example.com
|
|
110
|
+
exchange_name: staging_exchange
|
|
111
|
+
|
|
112
|
+
production:
|
|
113
|
+
<<: *defaults
|
|
114
|
+
host: <%= ENV.fetch('PRODUCTION_AMQP_HOST') %>
|
|
115
|
+
exchange_name: production_exchange
|
|
116
|
+
connection_pool_size: 10
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Usage:**
|
|
120
|
+
```ruby
|
|
121
|
+
BunnyFarm.config do
|
|
122
|
+
env Rails.env
|
|
123
|
+
bunny_file 'config/bunny.yml.erb'
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Programmatic Configuration
|
|
128
|
+
|
|
129
|
+
Ruby code-based configuration:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
BunnyFarm.config do
|
|
133
|
+
# Basic settings
|
|
134
|
+
app_id 'order_processor'
|
|
135
|
+
env Rails.env
|
|
136
|
+
|
|
137
|
+
# Connection settings
|
|
138
|
+
host 'amqp.example.com'
|
|
139
|
+
port 5672
|
|
140
|
+
vhost '/orders'
|
|
141
|
+
|
|
142
|
+
# Queue configuration
|
|
143
|
+
exchange_name 'order_exchange'
|
|
144
|
+
queue_name 'order_processing'
|
|
145
|
+
routing_key 'OrderMessage.*'
|
|
146
|
+
|
|
147
|
+
# Advanced options
|
|
148
|
+
connection_pool_size 5
|
|
149
|
+
heartbeat_interval 30
|
|
150
|
+
|
|
151
|
+
# Message options
|
|
152
|
+
message_options do
|
|
153
|
+
persistent true
|
|
154
|
+
mandatory false
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Queue options
|
|
158
|
+
queue_options do
|
|
159
|
+
durable true
|
|
160
|
+
auto_delete false
|
|
161
|
+
arguments({
|
|
162
|
+
'x-message-ttl' => 300_000, # 5 minutes
|
|
163
|
+
'x-max-length' => 10_000
|
|
164
|
+
})
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Configuration Patterns
|
|
170
|
+
|
|
171
|
+
### Multi-Environment Setup
|
|
172
|
+
|
|
173
|
+
**Rails Application:**
|
|
174
|
+
```ruby
|
|
175
|
+
# config/environments/development.rb
|
|
176
|
+
BunnyFarm.config do
|
|
177
|
+
env 'development'
|
|
178
|
+
log_level :debug
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# config/environments/staging.rb
|
|
182
|
+
BunnyFarm.config do
|
|
183
|
+
env 'staging'
|
|
184
|
+
bunny_file Rails.root.join('config', 'rabbitmq.yml')
|
|
185
|
+
log_level :info
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# config/environments/production.rb
|
|
189
|
+
BunnyFarm.config do
|
|
190
|
+
env 'production'
|
|
191
|
+
bunny_file Rails.root.join('config', 'rabbitmq.yml')
|
|
192
|
+
log_level :warn
|
|
193
|
+
|
|
194
|
+
# Production-specific settings
|
|
195
|
+
connection_pool_size 10
|
|
196
|
+
heartbeat_interval 60
|
|
197
|
+
connection_timeout 30
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Docker Configuration
|
|
202
|
+
|
|
203
|
+
**docker-compose.yml:**
|
|
204
|
+
```yaml
|
|
205
|
+
version: '3.8'
|
|
206
|
+
services:
|
|
207
|
+
app:
|
|
208
|
+
build: .
|
|
209
|
+
environment:
|
|
210
|
+
AMQP_HOST: rabbitmq
|
|
211
|
+
AMQP_USER: app
|
|
212
|
+
AMQP_PASS: ${RABBITMQ_PASSWORD}
|
|
213
|
+
AMQP_EXCHANGE: ${APP_EXCHANGE:-app_exchange}
|
|
214
|
+
AMQP_QUEUE: ${APP_QUEUE:-app_queue}
|
|
215
|
+
RAILS_ENV: production
|
|
216
|
+
depends_on:
|
|
217
|
+
- rabbitmq
|
|
218
|
+
|
|
219
|
+
rabbitmq:
|
|
220
|
+
image: rabbitmq:3-management
|
|
221
|
+
environment:
|
|
222
|
+
RABBITMQ_DEFAULT_USER: app
|
|
223
|
+
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Kubernetes Configuration
|
|
227
|
+
|
|
228
|
+
**ConfigMap:**
|
|
229
|
+
```yaml
|
|
230
|
+
apiVersion: v1
|
|
231
|
+
kind: ConfigMap
|
|
232
|
+
metadata:
|
|
233
|
+
name: bunny-farm-config
|
|
234
|
+
data:
|
|
235
|
+
AMQP_HOST: "rabbitmq-service"
|
|
236
|
+
AMQP_EXCHANGE: "production_exchange"
|
|
237
|
+
AMQP_QUEUE: "production_queue"
|
|
238
|
+
AMQP_APP_NAME: "order-processor"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Secret:**
|
|
242
|
+
```yaml
|
|
243
|
+
apiVersion: v1
|
|
244
|
+
kind: Secret
|
|
245
|
+
metadata:
|
|
246
|
+
name: bunny-farm-secret
|
|
247
|
+
type: Opaque
|
|
248
|
+
data:
|
|
249
|
+
AMQP_USER: YXBwX3VzZXI= # base64 encoded
|
|
250
|
+
AMQP_PASS: c2VjcmV0X3Bhc3M= # base64 encoded
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Deployment:**
|
|
254
|
+
```yaml
|
|
255
|
+
apiVersion: apps/v1
|
|
256
|
+
kind: Deployment
|
|
257
|
+
metadata:
|
|
258
|
+
name: order-processor
|
|
259
|
+
spec:
|
|
260
|
+
template:
|
|
261
|
+
spec:
|
|
262
|
+
containers:
|
|
263
|
+
- name: app
|
|
264
|
+
image: order-processor:latest
|
|
265
|
+
envFrom:
|
|
266
|
+
- configMapRef:
|
|
267
|
+
name: bunny-farm-config
|
|
268
|
+
- secretRef:
|
|
269
|
+
name: bunny-farm-secret
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Advanced Configuration Features
|
|
273
|
+
|
|
274
|
+
### Configuration Validation
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
BunnyFarm.config do
|
|
278
|
+
# Validate required settings
|
|
279
|
+
required_env_vars = %w[AMQP_HOST AMQP_USER AMQP_PASS]
|
|
280
|
+
missing = required_env_vars.select { |var| ENV[var].nil? }
|
|
281
|
+
|
|
282
|
+
raise "Missing required environment variables: #{missing.join(', ')}" if missing.any?
|
|
283
|
+
|
|
284
|
+
# Set configuration
|
|
285
|
+
host ENV['AMQP_HOST']
|
|
286
|
+
user ENV['AMQP_USER']
|
|
287
|
+
pass ENV['AMQP_PASS']
|
|
288
|
+
end
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Conditional Configuration
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
BunnyFarm.config do
|
|
295
|
+
# Base configuration
|
|
296
|
+
app_id 'message_processor'
|
|
297
|
+
|
|
298
|
+
# Environment-specific
|
|
299
|
+
case ENV['RAILS_ENV']
|
|
300
|
+
when 'development'
|
|
301
|
+
log_level :debug
|
|
302
|
+
queue_name 'dev_queue'
|
|
303
|
+
when 'test'
|
|
304
|
+
log_level :fatal
|
|
305
|
+
queue_name "test_queue_#{ENV['TEST_ENV_NUMBER']}"
|
|
306
|
+
when 'production'
|
|
307
|
+
log_level :info
|
|
308
|
+
connection_pool_size 20
|
|
309
|
+
queue_name 'prod_queue'
|
|
310
|
+
|
|
311
|
+
# Enable monitoring in production
|
|
312
|
+
monitoring_enabled true
|
|
313
|
+
health_check_port 8080
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Feature flags
|
|
317
|
+
if ENV['ENABLE_DEAD_LETTERS'] == 'true'
|
|
318
|
+
dead_letter_exchange 'failed_messages'
|
|
319
|
+
dead_letter_routing_key 'failed.#'
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Runtime Configuration Updates
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
class ConfigurableWorker
|
|
328
|
+
def initialize
|
|
329
|
+
@config_check_interval = 60 # Check every minute
|
|
330
|
+
@last_config_check = Time.current
|
|
331
|
+
setup_configuration
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def process_messages
|
|
335
|
+
loop do
|
|
336
|
+
check_configuration_updates if should_check_config?
|
|
337
|
+
|
|
338
|
+
BunnyFarm.manage_single_message
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
private
|
|
343
|
+
|
|
344
|
+
def should_check_config?
|
|
345
|
+
Time.current - @last_config_check > @config_check_interval
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def check_configuration_updates
|
|
349
|
+
new_config = load_config_from_source
|
|
350
|
+
|
|
351
|
+
if config_changed?(new_config)
|
|
352
|
+
logger.info "Configuration updated, reloading..."
|
|
353
|
+
update_configuration(new_config)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
@last_config_check = Time.current
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def config_changed?(new_config)
|
|
360
|
+
@current_config_hash != new_config.hash
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def update_configuration(new_config)
|
|
364
|
+
BunnyFarm.reconfigure(new_config)
|
|
365
|
+
@current_config_hash = new_config.hash
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Configuration Security
|
|
371
|
+
|
|
372
|
+
### Secret Management
|
|
373
|
+
|
|
374
|
+
Never store secrets in code or version control:
|
|
375
|
+
|
|
376
|
+
```ruby
|
|
377
|
+
# Good: Use environment variables or secret management
|
|
378
|
+
BunnyFarm.config do
|
|
379
|
+
user ENV['AMQP_USER']
|
|
380
|
+
pass ENV['AMQP_PASS'] # From Kubernetes secret, HashiCorp Vault, etc.
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Avoid: Hardcoded secrets
|
|
384
|
+
BunnyFarm.config do
|
|
385
|
+
user 'admin'
|
|
386
|
+
pass 'secret123' # Don't do this!
|
|
387
|
+
end
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Encrypted Configuration
|
|
391
|
+
|
|
392
|
+
For sensitive configuration files:
|
|
393
|
+
|
|
394
|
+
```ruby
|
|
395
|
+
require 'openssl'
|
|
396
|
+
|
|
397
|
+
class EncryptedConfig
|
|
398
|
+
def self.load(file_path, key)
|
|
399
|
+
encrypted_data = File.read(file_path)
|
|
400
|
+
cipher = OpenSSL::Cipher.new('AES-256-CBC')
|
|
401
|
+
cipher.decrypt
|
|
402
|
+
cipher.key = key
|
|
403
|
+
|
|
404
|
+
decrypted_data = cipher.update(encrypted_data) + cipher.final
|
|
405
|
+
YAML.safe_load(decrypted_data)
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Usage
|
|
410
|
+
config_key = ENV['CONFIG_ENCRYPTION_KEY']
|
|
411
|
+
config = EncryptedConfig.load('config/encrypted_bunny.yml', config_key)
|
|
412
|
+
|
|
413
|
+
BunnyFarm.config do
|
|
414
|
+
host config['host']
|
|
415
|
+
user config['user']
|
|
416
|
+
pass config['pass']
|
|
417
|
+
end
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Monitoring Configuration
|
|
421
|
+
|
|
422
|
+
### Health Checks
|
|
423
|
+
|
|
424
|
+
```ruby
|
|
425
|
+
BunnyFarm.config do
|
|
426
|
+
# Enable health check endpoint
|
|
427
|
+
health_check_enabled true
|
|
428
|
+
health_check_port 8080
|
|
429
|
+
health_check_path '/health'
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Health check responds with:
|
|
433
|
+
# GET /health
|
|
434
|
+
# {
|
|
435
|
+
# "status": "healthy",
|
|
436
|
+
# "rabbitmq_connection": "ok",
|
|
437
|
+
# "queue_depth": 42,
|
|
438
|
+
# "last_message_processed": "2024-01-15T10:30:00Z"
|
|
439
|
+
# }
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Metrics and Logging
|
|
443
|
+
|
|
444
|
+
```ruby
|
|
445
|
+
BunnyFarm.config do
|
|
446
|
+
# Detailed logging
|
|
447
|
+
log_level ENV['LOG_LEVEL']&.downcase&.to_sym || :info
|
|
448
|
+
log_format :json # For structured logging
|
|
449
|
+
|
|
450
|
+
# Metrics
|
|
451
|
+
metrics_enabled true
|
|
452
|
+
metrics_port 9090
|
|
453
|
+
metrics_path '/metrics'
|
|
454
|
+
|
|
455
|
+
# Custom metric tags
|
|
456
|
+
metric_tags({
|
|
457
|
+
environment: ENV['RAILS_ENV'],
|
|
458
|
+
region: ENV['AWS_REGION'],
|
|
459
|
+
instance: ENV['HOSTNAME']
|
|
460
|
+
})
|
|
461
|
+
end
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
## Best Practices
|
|
465
|
+
|
|
466
|
+
### 1. Layer Configuration Appropriately
|
|
467
|
+
|
|
468
|
+
```ruby
|
|
469
|
+
# Layer 1: Sensible defaults
|
|
470
|
+
BunnyFarm.config do
|
|
471
|
+
host 'localhost'
|
|
472
|
+
port 5672
|
|
473
|
+
heartbeat_interval 30
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Layer 2: Environment-specific overrides
|
|
477
|
+
BunnyFarm.config do
|
|
478
|
+
bunny_file "config/#{Rails.env}.yml"
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Layer 3: Runtime overrides
|
|
482
|
+
BunnyFarm.config do
|
|
483
|
+
host ENV['AMQP_HOST'] if ENV['AMQP_HOST']
|
|
484
|
+
log_level ENV['LOG_LEVEL'].to_sym if ENV['LOG_LEVEL']
|
|
485
|
+
end
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### 2. Validate Critical Configuration
|
|
489
|
+
|
|
490
|
+
```ruby
|
|
491
|
+
BunnyFarm.config do
|
|
492
|
+
host ENV.fetch('AMQP_HOST') # Will raise if missing
|
|
493
|
+
|
|
494
|
+
port_value = ENV['AMQP_PORT']&.to_i || 5672
|
|
495
|
+
raise "Invalid port: #{port_value}" unless (1..65535).include?(port_value)
|
|
496
|
+
port port_value
|
|
497
|
+
end
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### 3. Document Configuration Options
|
|
501
|
+
|
|
502
|
+
```ruby
|
|
503
|
+
# config/bunny_farm.rb
|
|
504
|
+
BunnyFarm.config do
|
|
505
|
+
# Connection settings - required in production
|
|
506
|
+
host ENV['AMQP_HOST'] # RabbitMQ server hostname
|
|
507
|
+
port ENV['AMQP_PORT']&.to_i # RabbitMQ server port (default: 5672)
|
|
508
|
+
vhost ENV['AMQP_VHOST'] # Virtual host (default: '/')
|
|
509
|
+
|
|
510
|
+
# Authentication - use secrets in production
|
|
511
|
+
user ENV['AMQP_USER'] # Username (default: 'guest')
|
|
512
|
+
pass ENV['AMQP_PASS'] # Password (default: 'guest')
|
|
513
|
+
|
|
514
|
+
# Message routing
|
|
515
|
+
exchange_name ENV['AMQP_EXCHANGE'] || 'app_exchange'
|
|
516
|
+
queue_name ENV['AMQP_QUEUE'] || 'app_queue'
|
|
517
|
+
routing_key ENV['AMQP_ROUTING_KEY'] || '#'
|
|
518
|
+
end
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Next Steps
|
|
522
|
+
|
|
523
|
+
Configuration mastery enables:
|
|
524
|
+
|
|
525
|
+
- **[Environment-specific deployment](../architecture/overview.md)**
|
|
526
|
+
- **[Performance tuning](../architecture/scaling.md)** through configuration
|
|
527
|
+
- **[Security hardening](../development/contributing.md)** with proper secret management
|
|
528
|
+
- **[Monitoring and observability](error-handling.md)** through configuration
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Error Handling
|
|
2
|
+
|
|
3
|
+
BunnyFarm provides comprehensive error handling mechanisms that help build resilient message processing systems with proper failure management and recovery strategies.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Built-in Error Tracking
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
class OrderMessage < BunnyFarm::Message
|
|
11
|
+
def process
|
|
12
|
+
validate_order
|
|
13
|
+
return unless successful?
|
|
14
|
+
|
|
15
|
+
process_payment
|
|
16
|
+
return unless successful?
|
|
17
|
+
|
|
18
|
+
fulfill_order
|
|
19
|
+
return unless successful?
|
|
20
|
+
|
|
21
|
+
success!
|
|
22
|
+
successful?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def validate_order
|
|
28
|
+
failure("Order ID required") unless @items[:order_id]
|
|
29
|
+
failure("Customer email required") unless @items[:customer_email]
|
|
30
|
+
|
|
31
|
+
success! if errors.empty?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Error Recovery Patterns
|
|
37
|
+
|
|
38
|
+
### Retry with Exponential Backoff
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
def process_with_retry
|
|
42
|
+
attempt = @items[:attempt] || 1
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
risky_operation
|
|
46
|
+
success!
|
|
47
|
+
rescue RetryableError => e
|
|
48
|
+
if attempt < 3
|
|
49
|
+
delay = 2 ** attempt
|
|
50
|
+
retry_message = self.class.new(@items)
|
|
51
|
+
retry_message[:attempt] = attempt + 1
|
|
52
|
+
retry_message.publish_delayed('process_with_retry', delay: delay)
|
|
53
|
+
success! # Don't fail original message
|
|
54
|
+
else
|
|
55
|
+
failure("Failed after #{attempt} attempts: #{e.message}")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
successful?
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Dead Letter Handling
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
class FailedMessage < BunnyFarm::Message
|
|
67
|
+
actions :investigate, :retry, :archive
|
|
68
|
+
|
|
69
|
+
def investigate
|
|
70
|
+
puts "Investigating failed message: #{@items[:original_error]}"
|
|
71
|
+
success!
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Best Practices
|
|
77
|
+
|
|
78
|
+
1. Always handle exceptions gracefully
|
|
79
|
+
2. Provide meaningful error messages
|
|
80
|
+
3. Implement retry logic for transient failures
|
|
81
|
+
4. Use dead letter queues for persistent failures
|
|
82
|
+
5. Log errors for debugging and monitoring
|