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,1091 @@
|
|
1
|
+
# Redis Queue Transport - Production Guide
|
2
|
+
|
3
|
+
This guide covers deploying Redis Queue Transport in production environments, including configuration, monitoring, scaling, and operational best practices.
|
4
|
+
|
5
|
+
## Production Architecture
|
6
|
+
|
7
|
+
### Recommended Infrastructure
|
8
|
+
|
9
|
+
```
|
10
|
+
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
11
|
+
│ Application │ │ Redis │ │ Monitoring │
|
12
|
+
│ Servers │◄──►│ Cluster │◄──►│ Stack │
|
13
|
+
│ │ │ │ │ │
|
14
|
+
│ • Rails Apps │ │ • Master/Replica │ │ • Prometheus │
|
15
|
+
│ • Workers │ │ • Sentinel │ │ • Grafana │
|
16
|
+
│ • Background │ │ • Persistence │ │ • AlertManager │
|
17
|
+
│ Jobs │ │ • Memory Opt │ │ • Logs │
|
18
|
+
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
19
|
+
```
|
20
|
+
|
21
|
+
### Infrastructure Components
|
22
|
+
|
23
|
+
1. **Application Layer**: Rails applications, worker processes
|
24
|
+
2. **Redis Layer**: Clustered Redis with persistence and monitoring
|
25
|
+
3. **Load Balancers**: HAProxy/nginx for application load balancing
|
26
|
+
4. **Monitoring**: Comprehensive observability stack
|
27
|
+
5. **Alerting**: Proactive issue detection and notification
|
28
|
+
|
29
|
+
## Redis Configuration
|
30
|
+
|
31
|
+
### Production Redis Settings
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# config/redis.conf
|
35
|
+
# Memory and performance optimization
|
36
|
+
maxmemory 4gb
|
37
|
+
maxmemory-policy allkeys-lru
|
38
|
+
tcp-keepalive 60
|
39
|
+
timeout 0
|
40
|
+
|
41
|
+
# Persistence for queue durability
|
42
|
+
save 900 1 # Save if at least 1 change in 15 minutes
|
43
|
+
save 300 10 # Save if at least 10 changes in 5 minutes
|
44
|
+
save 60 10000 # Save if at least 10k changes in 1 minute
|
45
|
+
|
46
|
+
# AOF for maximum durability
|
47
|
+
appendonly yes
|
48
|
+
appendfsync everysec
|
49
|
+
auto-aof-rewrite-percentage 100
|
50
|
+
auto-aof-rewrite-min-size 64mb
|
51
|
+
|
52
|
+
# Network and connection settings
|
53
|
+
tcp-backlog 511
|
54
|
+
bind 0.0.0.0
|
55
|
+
port 6379
|
56
|
+
protected-mode yes
|
57
|
+
requirepass your_secure_password
|
58
|
+
|
59
|
+
# Logging
|
60
|
+
loglevel notice
|
61
|
+
logfile /var/log/redis/redis-server.log
|
62
|
+
syslog-enabled yes
|
63
|
+
|
64
|
+
# Performance tuning
|
65
|
+
hash-max-ziplist-entries 512
|
66
|
+
hash-max-ziplist-value 64
|
67
|
+
list-max-ziplist-size -2
|
68
|
+
list-compress-depth 0
|
69
|
+
```
|
70
|
+
|
71
|
+
### Redis Cluster Setup
|
72
|
+
|
73
|
+
```yaml
|
74
|
+
# docker-compose.yml for Redis cluster
|
75
|
+
version: '3.8'
|
76
|
+
services:
|
77
|
+
redis-master:
|
78
|
+
image: redis:7-alpine
|
79
|
+
command: redis-server /etc/redis/redis.conf
|
80
|
+
volumes:
|
81
|
+
- ./redis-master.conf:/etc/redis/redis.conf
|
82
|
+
- redis-master-data:/data
|
83
|
+
ports:
|
84
|
+
- "6379:6379"
|
85
|
+
networks:
|
86
|
+
- redis-network
|
87
|
+
|
88
|
+
redis-replica-1:
|
89
|
+
image: redis:7-alpine
|
90
|
+
command: redis-server /etc/redis/redis.conf
|
91
|
+
volumes:
|
92
|
+
- ./redis-replica.conf:/etc/redis/redis.conf
|
93
|
+
- redis-replica-1-data:/data
|
94
|
+
depends_on:
|
95
|
+
- redis-master
|
96
|
+
networks:
|
97
|
+
- redis-network
|
98
|
+
|
99
|
+
redis-replica-2:
|
100
|
+
image: redis:7-alpine
|
101
|
+
command: redis-server /etc/redis/redis.conf
|
102
|
+
volumes:
|
103
|
+
- ./redis-replica.conf:/etc/redis/redis.conf
|
104
|
+
- redis-replica-2-data:/data
|
105
|
+
depends_on:
|
106
|
+
- redis-master
|
107
|
+
networks:
|
108
|
+
- redis-network
|
109
|
+
|
110
|
+
redis-sentinel-1:
|
111
|
+
image: redis:7-alpine
|
112
|
+
command: redis-sentinel /etc/redis/sentinel.conf
|
113
|
+
volumes:
|
114
|
+
- ./sentinel.conf:/etc/redis/sentinel.conf
|
115
|
+
depends_on:
|
116
|
+
- redis-master
|
117
|
+
networks:
|
118
|
+
- redis-network
|
119
|
+
|
120
|
+
volumes:
|
121
|
+
redis-master-data:
|
122
|
+
redis-replica-1-data:
|
123
|
+
redis-replica-2-data:
|
124
|
+
|
125
|
+
networks:
|
126
|
+
redis-network:
|
127
|
+
driver: bridge
|
128
|
+
```
|
129
|
+
|
130
|
+
## SmartMessage Configuration
|
131
|
+
|
132
|
+
### Production Transport Configuration
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
# config/environments/production.rb
|
136
|
+
SmartMessage.configure do |config|
|
137
|
+
config.transport = :redis_queue
|
138
|
+
config.transport_options = {
|
139
|
+
# Connection settings
|
140
|
+
url: ENV.fetch('REDIS_URL', 'redis://redis.internal:6379'),
|
141
|
+
db: ENV.fetch('REDIS_DB', '0').to_i,
|
142
|
+
password: ENV['REDIS_PASSWORD'],
|
143
|
+
|
144
|
+
# Queue configuration
|
145
|
+
queue_prefix: "#{Rails.application.class.module_parent_name.downcase}.#{Rails.env}",
|
146
|
+
consumer_group: "#{Rails.application.class.module_parent_name.downcase}_workers",
|
147
|
+
consumer_id: "#{Socket.gethostname}_#{Process.pid}",
|
148
|
+
|
149
|
+
# Performance tuning
|
150
|
+
block_time: 5000, # 5 second blocking timeout
|
151
|
+
max_queue_length: 100000, # Large queue capacity
|
152
|
+
batch_size: 10, # Process in batches
|
153
|
+
|
154
|
+
# Reliability settings
|
155
|
+
max_retries: 5,
|
156
|
+
retry_delay: 30, # 30 seconds between retries
|
157
|
+
exponential_backoff: true,
|
158
|
+
dead_letter_queue: true,
|
159
|
+
dead_letter_prefix: 'dlq',
|
160
|
+
|
161
|
+
# Connection pooling
|
162
|
+
pool_size: ENV.fetch('REDIS_POOL_SIZE', '10').to_i,
|
163
|
+
pool_timeout: ENV.fetch('REDIS_POOL_TIMEOUT', '5').to_i,
|
164
|
+
|
165
|
+
# Circuit breaker
|
166
|
+
circuit_breaker: true,
|
167
|
+
failure_threshold: 10,
|
168
|
+
recovery_timeout: 120,
|
169
|
+
|
170
|
+
# Monitoring
|
171
|
+
enable_metrics: true,
|
172
|
+
metrics_interval: 60,
|
173
|
+
|
174
|
+
# Security
|
175
|
+
ssl_params: Rails.env.production? ? { verify_mode: OpenSSL::SSL::VERIFY_PEER } : nil
|
176
|
+
}
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
### Environment-Specific Settings
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
# config/smartmessage.rb
|
184
|
+
class SmartMessageConfig
|
185
|
+
ENVIRONMENT_CONFIGS = {
|
186
|
+
development: {
|
187
|
+
block_time: 1000, # Fast for development
|
188
|
+
max_queue_length: 1000, # Small queues
|
189
|
+
debug: true,
|
190
|
+
pool_size: 2
|
191
|
+
},
|
192
|
+
|
193
|
+
test: {
|
194
|
+
block_time: 100, # Very fast for tests
|
195
|
+
max_queue_length: 100, # Tiny queues
|
196
|
+
db: 15, # Test database
|
197
|
+
pool_size: 1
|
198
|
+
},
|
199
|
+
|
200
|
+
staging: {
|
201
|
+
block_time: 3000, # Medium performance
|
202
|
+
max_queue_length: 10000, # Medium queues
|
203
|
+
max_retries: 3,
|
204
|
+
pool_size: 5
|
205
|
+
},
|
206
|
+
|
207
|
+
production: {
|
208
|
+
block_time: 5000, # Optimized for throughput
|
209
|
+
max_queue_length: 100000,# Large queues
|
210
|
+
max_retries: 5,
|
211
|
+
pool_size: 20,
|
212
|
+
circuit_breaker: true,
|
213
|
+
dead_letter_queue: true
|
214
|
+
}
|
215
|
+
}.freeze
|
216
|
+
|
217
|
+
def self.configure_for_environment(env = Rails.env)
|
218
|
+
base_config = SmartMessage.configuration.transport_options || {}
|
219
|
+
env_config = ENVIRONMENT_CONFIGS[env.to_sym] || {}
|
220
|
+
|
221
|
+
SmartMessage.configure do |config|
|
222
|
+
config.transport_options = base_config.merge(env_config)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Apply environment-specific configuration
|
228
|
+
SmartMessageConfig.configure_for_environment
|
229
|
+
```
|
230
|
+
|
231
|
+
## Scaling and Performance
|
232
|
+
|
233
|
+
### Horizontal Scaling
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
# config/initializers/smart_message_workers.rb
|
237
|
+
class SmartMessageWorkers
|
238
|
+
def self.start_for_environment
|
239
|
+
worker_config = case Rails.env
|
240
|
+
when 'production'
|
241
|
+
production_workers
|
242
|
+
when 'staging'
|
243
|
+
staging_workers
|
244
|
+
else
|
245
|
+
development_workers
|
246
|
+
end
|
247
|
+
|
248
|
+
start_workers(worker_config)
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
def self.production_workers
|
254
|
+
{
|
255
|
+
# High-volume message processing
|
256
|
+
general_workers: {
|
257
|
+
count: ENV.fetch('GENERAL_WORKERS', '8').to_i,
|
258
|
+
consumer_group: 'general_workers',
|
259
|
+
patterns: ['#.*.general_service', '#.*.default']
|
260
|
+
},
|
261
|
+
|
262
|
+
# Critical business processes
|
263
|
+
order_workers: {
|
264
|
+
count: ENV.fetch('ORDER_WORKERS', '4').to_i,
|
265
|
+
consumer_group: 'order_workers',
|
266
|
+
patterns: ['order.#.*.*', '#.*.order_service']
|
267
|
+
},
|
268
|
+
|
269
|
+
# Payment processing
|
270
|
+
payment_workers: {
|
271
|
+
count: ENV.fetch('PAYMENT_WORKERS', '3').to_i,
|
272
|
+
consumer_group: 'payment_workers',
|
273
|
+
patterns: ['payment.#.*.*', '#.*.payment_service']
|
274
|
+
},
|
275
|
+
|
276
|
+
# Email and notifications
|
277
|
+
notification_workers: {
|
278
|
+
count: ENV.fetch('NOTIFICATION_WORKERS', '2').to_i,
|
279
|
+
consumer_group: 'notification_workers',
|
280
|
+
patterns: ['notification.#.*.*', '#.*.notification_service']
|
281
|
+
},
|
282
|
+
|
283
|
+
# Analytics and reporting
|
284
|
+
analytics_workers: {
|
285
|
+
count: ENV.fetch('ANALYTICS_WORKERS', '2').to_i,
|
286
|
+
consumer_group: 'analytics_workers',
|
287
|
+
patterns: ['analytics.#.*.*', '#.*.analytics_service']
|
288
|
+
}
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
def self.start_workers(worker_config)
|
293
|
+
worker_config.each do |worker_type, config|
|
294
|
+
Rails.logger.info "Starting #{config[:count]} #{worker_type} workers"
|
295
|
+
|
296
|
+
config[:count].times do |i|
|
297
|
+
Thread.new do
|
298
|
+
start_worker(worker_type, i + 1, config)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.start_worker(worker_type, worker_id, config)
|
305
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
306
|
+
consumer_group: config[:consumer_group],
|
307
|
+
consumer_id: "#{worker_type}_#{worker_id}_#{Socket.gethostname}"
|
308
|
+
)
|
309
|
+
|
310
|
+
config[:patterns].each do |pattern|
|
311
|
+
transport.subscribe_pattern(pattern) do |message_class, message_data|
|
312
|
+
Rails.logger.info "[#{worker_type}_#{worker_id}] Processing: #{message_class}"
|
313
|
+
|
314
|
+
begin
|
315
|
+
# Process message with timeout
|
316
|
+
Timeout::timeout(30) do
|
317
|
+
process_message(message_class, message_data)
|
318
|
+
end
|
319
|
+
rescue Timeout::Error
|
320
|
+
Rails.logger.error "[#{worker_type}_#{worker_id}] Timeout processing: #{message_class}"
|
321
|
+
raise SmartMessage::Errors::RetryableError, 'Processing timeout'
|
322
|
+
rescue => e
|
323
|
+
Rails.logger.error "[#{worker_type}_#{worker_id}] Error: #{e.message}"
|
324
|
+
raise
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
Rails.logger.info "[#{worker_type}_#{worker_id}] Worker started"
|
330
|
+
|
331
|
+
# Keep worker alive
|
332
|
+
loop { sleep 1 }
|
333
|
+
rescue => e
|
334
|
+
Rails.logger.error "[#{worker_type}_#{worker_id}] Worker crashed: #{e.message}"
|
335
|
+
# Restart worker after delay
|
336
|
+
sleep 5
|
337
|
+
retry
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
# Auto-start workers in production
|
342
|
+
if Rails.env.production?
|
343
|
+
Thread.new { SmartMessageWorkers.start_for_environment }
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
### Vertical Scaling
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
# config/initializers/performance_optimization.rb
|
351
|
+
module SmartMessageOptimization
|
352
|
+
def self.optimize_for_production
|
353
|
+
# Optimize Redis connection pool
|
354
|
+
configure_connection_pool
|
355
|
+
|
356
|
+
# Set up message batching
|
357
|
+
configure_batching
|
358
|
+
|
359
|
+
# Enable compression for large messages
|
360
|
+
configure_compression
|
361
|
+
|
362
|
+
# Set up performance monitoring
|
363
|
+
configure_monitoring
|
364
|
+
end
|
365
|
+
|
366
|
+
private
|
367
|
+
|
368
|
+
def self.configure_connection_pool
|
369
|
+
SmartMessage.configure do |config|
|
370
|
+
config.transport_options.merge!({
|
371
|
+
pool_size: [ENV.fetch('MAX_THREADS', '20').to_i, 50].min,
|
372
|
+
pool_timeout: 10,
|
373
|
+
reconnect_attempts: 3,
|
374
|
+
reconnect_delay: 1
|
375
|
+
})
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def self.configure_batching
|
380
|
+
# Process messages in batches for better throughput
|
381
|
+
SmartMessage::Transport::RedisQueueTransport.class_eval do
|
382
|
+
def process_message_batch(messages)
|
383
|
+
messages.each do |message_class, message_data|
|
384
|
+
begin
|
385
|
+
receive(message_class, message_data)
|
386
|
+
rescue => e
|
387
|
+
Rails.logger.error "Batch processing error: #{e.message}"
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def self.configure_compression
|
395
|
+
# Enable compression for messages over 1KB
|
396
|
+
SmartMessage.configure do |config|
|
397
|
+
config.serializer_options = {
|
398
|
+
compress_threshold: 1024,
|
399
|
+
compression_method: :gzip
|
400
|
+
}
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def self.configure_monitoring
|
405
|
+
# Set up performance metrics collection
|
406
|
+
ActiveSupport::Notifications.subscribe('smartmessage.message_processed') do |*args|
|
407
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
408
|
+
|
409
|
+
# Log processing time
|
410
|
+
Rails.logger.info "Message processed in #{event.duration}ms: #{event.payload[:message_class]}"
|
411
|
+
|
412
|
+
# Send to metrics collector
|
413
|
+
if defined?(Prometheus)
|
414
|
+
SmartMessageMetrics.record_processing_time(event.duration, event.payload[:message_class])
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
SmartMessageOptimization.optimize_for_production if Rails.env.production?
|
421
|
+
```
|
422
|
+
|
423
|
+
## Monitoring and Observability
|
424
|
+
|
425
|
+
### Application Monitoring
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
# app/services/smart_message_monitor.rb
|
429
|
+
class SmartMessageMonitor
|
430
|
+
include Singleton
|
431
|
+
|
432
|
+
def initialize
|
433
|
+
@transport = SmartMessage::Transport::RedisQueueTransport.new
|
434
|
+
@metrics = {}
|
435
|
+
start_monitoring
|
436
|
+
end
|
437
|
+
|
438
|
+
def start_monitoring
|
439
|
+
Thread.new { monitor_queues }
|
440
|
+
Thread.new { monitor_workers }
|
441
|
+
Thread.new { monitor_performance }
|
442
|
+
end
|
443
|
+
|
444
|
+
private
|
445
|
+
|
446
|
+
def monitor_queues
|
447
|
+
loop do
|
448
|
+
stats = @transport.queue_stats
|
449
|
+
|
450
|
+
stats.each do |queue_name, info|
|
451
|
+
queue_length = info[:length]
|
452
|
+
consumer_count = info[:consumers] || 0
|
453
|
+
|
454
|
+
# Record metrics
|
455
|
+
@metrics["queue.#{queue_name}.length"] = queue_length
|
456
|
+
@metrics["queue.#{queue_name}.consumers"] = consumer_count
|
457
|
+
|
458
|
+
# Check for alerts
|
459
|
+
if queue_length > 1000
|
460
|
+
alert_high_queue_length(queue_name, queue_length)
|
461
|
+
end
|
462
|
+
|
463
|
+
if queue_length > 0 && consumer_count == 0
|
464
|
+
alert_no_consumers(queue_name, queue_length)
|
465
|
+
end
|
466
|
+
|
467
|
+
if consumer_count > 20
|
468
|
+
alert_high_consumer_count(queue_name, consumer_count)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
# Update external metrics
|
473
|
+
update_external_metrics(@metrics)
|
474
|
+
|
475
|
+
sleep 30 # Check every 30 seconds
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def monitor_workers
|
480
|
+
loop do
|
481
|
+
worker_stats = collect_worker_stats
|
482
|
+
|
483
|
+
worker_stats.each do |worker_type, stats|
|
484
|
+
@metrics["workers.#{worker_type}.active"] = stats[:active]
|
485
|
+
@metrics["workers.#{worker_type}.processing"] = stats[:processing]
|
486
|
+
@metrics["workers.#{worker_type}.errors"] = stats[:errors]
|
487
|
+
end
|
488
|
+
|
489
|
+
sleep 60 # Check every minute
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
def monitor_performance
|
494
|
+
start_time = Time.now
|
495
|
+
processed_messages = 0
|
496
|
+
|
497
|
+
loop do
|
498
|
+
current_processed = get_total_processed_messages
|
499
|
+
duration = Time.now - start_time
|
500
|
+
|
501
|
+
if duration >= 60 # Calculate rate every minute
|
502
|
+
rate = (current_processed - processed_messages) / duration
|
503
|
+
@metrics['performance.messages_per_second'] = rate
|
504
|
+
|
505
|
+
processed_messages = current_processed
|
506
|
+
start_time = Time.now
|
507
|
+
end
|
508
|
+
|
509
|
+
sleep 10
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def alert_high_queue_length(queue_name, length)
|
514
|
+
Rails.logger.warn "HIGH QUEUE LENGTH: #{queue_name} has #{length} messages"
|
515
|
+
|
516
|
+
# Send to alerting system
|
517
|
+
if defined?(AlertManager)
|
518
|
+
AlertManager.alert(
|
519
|
+
severity: :warning,
|
520
|
+
message: "Queue #{queue_name} has #{length} messages",
|
521
|
+
tags: { queue: queue_name, type: :high_queue_length }
|
522
|
+
)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def alert_no_consumers(queue_name, length)
|
527
|
+
Rails.logger.error "NO CONSUMERS: #{queue_name} has #{length} messages but no consumers"
|
528
|
+
|
529
|
+
if defined?(AlertManager)
|
530
|
+
AlertManager.alert(
|
531
|
+
severity: :critical,
|
532
|
+
message: "Queue #{queue_name} has no consumers but #{length} pending messages",
|
533
|
+
tags: { queue: queue_name, type: :no_consumers }
|
534
|
+
)
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def update_external_metrics(metrics)
|
539
|
+
# Send to Prometheus
|
540
|
+
if defined?(Prometheus::Client)
|
541
|
+
metrics.each do |metric_name, value|
|
542
|
+
Prometheus::Client.registry.get(metric_name.to_sym)&.set(value)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
# Send to StatsD
|
547
|
+
if defined?(Statsd)
|
548
|
+
metrics.each do |metric_name, value|
|
549
|
+
$statsd&.gauge("smartmessage.#{metric_name}", value)
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# Send to CloudWatch
|
554
|
+
if defined?(Aws::CloudWatch)
|
555
|
+
# Implementation for CloudWatch metrics
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
# Start monitoring in production
|
561
|
+
SmartMessageMonitor.instance if Rails.env.production?
|
562
|
+
```
|
563
|
+
|
564
|
+
### Prometheus Metrics
|
565
|
+
|
566
|
+
```ruby
|
567
|
+
# lib/smart_message_metrics.rb
|
568
|
+
require 'prometheus/client'
|
569
|
+
|
570
|
+
module SmartMessageMetrics
|
571
|
+
def self.setup_metrics
|
572
|
+
registry = Prometheus::Client.registry
|
573
|
+
|
574
|
+
@message_processing_duration = registry.histogram(
|
575
|
+
:smartmessage_processing_duration_seconds,
|
576
|
+
docstring: 'Time spent processing messages',
|
577
|
+
labels: [:message_class, :worker_type]
|
578
|
+
)
|
579
|
+
|
580
|
+
@queue_length = registry.gauge(
|
581
|
+
:smartmessage_queue_length,
|
582
|
+
docstring: 'Current queue length',
|
583
|
+
labels: [:queue_name, :pattern]
|
584
|
+
)
|
585
|
+
|
586
|
+
@consumer_count = registry.gauge(
|
587
|
+
:smartmessage_consumer_count,
|
588
|
+
docstring: 'Number of active consumers',
|
589
|
+
labels: [:queue_name, :consumer_group]
|
590
|
+
)
|
591
|
+
|
592
|
+
@messages_processed_total = registry.counter(
|
593
|
+
:smartmessage_messages_processed_total,
|
594
|
+
docstring: 'Total number of messages processed',
|
595
|
+
labels: [:message_class, :status]
|
596
|
+
)
|
597
|
+
|
598
|
+
@connection_errors_total = registry.counter(
|
599
|
+
:smartmessage_connection_errors_total,
|
600
|
+
docstring: 'Total number of Redis connection errors',
|
601
|
+
labels: [:error_type]
|
602
|
+
)
|
603
|
+
end
|
604
|
+
|
605
|
+
def self.record_processing_time(duration_seconds, message_class, worker_type = 'unknown')
|
606
|
+
@message_processing_duration&.observe(duration_seconds, labels: {
|
607
|
+
message_class: message_class,
|
608
|
+
worker_type: worker_type
|
609
|
+
})
|
610
|
+
end
|
611
|
+
|
612
|
+
def self.update_queue_length(queue_name, length, pattern = '')
|
613
|
+
@queue_length&.set(length, labels: {
|
614
|
+
queue_name: queue_name,
|
615
|
+
pattern: pattern
|
616
|
+
})
|
617
|
+
end
|
618
|
+
|
619
|
+
def self.update_consumer_count(queue_name, count, consumer_group = '')
|
620
|
+
@consumer_count&.set(count, labels: {
|
621
|
+
queue_name: queue_name,
|
622
|
+
consumer_group: consumer_group
|
623
|
+
})
|
624
|
+
end
|
625
|
+
|
626
|
+
def self.increment_messages_processed(message_class, status = 'success')
|
627
|
+
@messages_processed_total&.increment(labels: {
|
628
|
+
message_class: message_class,
|
629
|
+
status: status
|
630
|
+
})
|
631
|
+
end
|
632
|
+
|
633
|
+
def self.increment_connection_errors(error_type)
|
634
|
+
@connection_errors_total&.increment(labels: {
|
635
|
+
error_type: error_type
|
636
|
+
})
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
# Initialize metrics
|
641
|
+
SmartMessageMetrics.setup_metrics if defined?(Prometheus::Client)
|
642
|
+
```
|
643
|
+
|
644
|
+
### Grafana Dashboard
|
645
|
+
|
646
|
+
```json
|
647
|
+
{
|
648
|
+
"dashboard": {
|
649
|
+
"id": null,
|
650
|
+
"title": "SmartMessage Redis Queue Transport",
|
651
|
+
"tags": ["smartmessage", "redis", "queues"],
|
652
|
+
"timezone": "browser",
|
653
|
+
"panels": [
|
654
|
+
{
|
655
|
+
"title": "Queue Lengths",
|
656
|
+
"type": "graph",
|
657
|
+
"targets": [
|
658
|
+
{
|
659
|
+
"expr": "smartmessage_queue_length",
|
660
|
+
"legendFormat": "{{ queue_name }}"
|
661
|
+
}
|
662
|
+
],
|
663
|
+
"yAxes": [
|
664
|
+
{
|
665
|
+
"label": "Messages",
|
666
|
+
"min": 0
|
667
|
+
}
|
668
|
+
]
|
669
|
+
},
|
670
|
+
{
|
671
|
+
"title": "Messages Processed per Second",
|
672
|
+
"type": "graph",
|
673
|
+
"targets": [
|
674
|
+
{
|
675
|
+
"expr": "rate(smartmessage_messages_processed_total[1m])",
|
676
|
+
"legendFormat": "{{ message_class }} ({{ status }})"
|
677
|
+
}
|
678
|
+
]
|
679
|
+
},
|
680
|
+
{
|
681
|
+
"title": "Processing Duration",
|
682
|
+
"type": "graph",
|
683
|
+
"targets": [
|
684
|
+
{
|
685
|
+
"expr": "histogram_quantile(0.95, smartmessage_processing_duration_seconds_bucket)",
|
686
|
+
"legendFormat": "95th percentile"
|
687
|
+
},
|
688
|
+
{
|
689
|
+
"expr": "histogram_quantile(0.50, smartmessage_processing_duration_seconds_bucket)",
|
690
|
+
"legendFormat": "50th percentile"
|
691
|
+
}
|
692
|
+
]
|
693
|
+
},
|
694
|
+
{
|
695
|
+
"title": "Active Consumers",
|
696
|
+
"type": "stat",
|
697
|
+
"targets": [
|
698
|
+
{
|
699
|
+
"expr": "sum(smartmessage_consumer_count)",
|
700
|
+
"legendFormat": "Total Consumers"
|
701
|
+
}
|
702
|
+
]
|
703
|
+
},
|
704
|
+
{
|
705
|
+
"title": "Connection Errors",
|
706
|
+
"type": "graph",
|
707
|
+
"targets": [
|
708
|
+
{
|
709
|
+
"expr": "rate(smartmessage_connection_errors_total[5m])",
|
710
|
+
"legendFormat": "{{ error_type }}"
|
711
|
+
}
|
712
|
+
]
|
713
|
+
}
|
714
|
+
]
|
715
|
+
}
|
716
|
+
}
|
717
|
+
```
|
718
|
+
|
719
|
+
## Security
|
720
|
+
|
721
|
+
### Authentication and Authorization
|
722
|
+
|
723
|
+
```ruby
|
724
|
+
# config/initializers/redis_security.rb
|
725
|
+
class RedisSecurityConfig
|
726
|
+
def self.configure_for_production
|
727
|
+
SmartMessage.configure do |config|
|
728
|
+
config.transport_options.merge!({
|
729
|
+
# Redis authentication
|
730
|
+
password: ENV.fetch('REDIS_PASSWORD'),
|
731
|
+
|
732
|
+
# SSL/TLS encryption
|
733
|
+
ssl: true,
|
734
|
+
ssl_params: {
|
735
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER,
|
736
|
+
ca_file: ENV['REDIS_CA_CERT_PATH'],
|
737
|
+
cert: OpenSSL::X509::Certificate.new(File.read(ENV['REDIS_CLIENT_CERT_PATH'])),
|
738
|
+
key: OpenSSL::PKey::RSA.new(File.read(ENV['REDIS_CLIENT_KEY_PATH']))
|
739
|
+
},
|
740
|
+
|
741
|
+
# Network security
|
742
|
+
bind: ENV.fetch('REDIS_BIND_ADDRESS', '127.0.0.1'),
|
743
|
+
|
744
|
+
# Message encryption
|
745
|
+
encrypt_messages: true,
|
746
|
+
encryption_key: ENV.fetch('MESSAGE_ENCRYPTION_KEY')
|
747
|
+
})
|
748
|
+
end
|
749
|
+
end
|
750
|
+
end
|
751
|
+
|
752
|
+
RedisSecurityConfig.configure_for_production if Rails.env.production?
|
753
|
+
```
|
754
|
+
|
755
|
+
### Message Encryption
|
756
|
+
|
757
|
+
```ruby
|
758
|
+
# lib/smart_message_encryption.rb
|
759
|
+
module SmartMessageEncryption
|
760
|
+
def self.encrypt_message(message_data)
|
761
|
+
key = ENV.fetch('MESSAGE_ENCRYPTION_KEY')
|
762
|
+
cipher = OpenSSL::Cipher.new('AES-256-GCM')
|
763
|
+
cipher.encrypt
|
764
|
+
cipher.key = Base64.decode64(key)
|
765
|
+
|
766
|
+
iv = cipher.random_iv
|
767
|
+
encrypted_data = cipher.update(message_data) + cipher.final
|
768
|
+
auth_tag = cipher.auth_tag
|
769
|
+
|
770
|
+
Base64.encode64({
|
771
|
+
iv: Base64.encode64(iv),
|
772
|
+
data: Base64.encode64(encrypted_data),
|
773
|
+
tag: Base64.encode64(auth_tag)
|
774
|
+
}.to_json)
|
775
|
+
end
|
776
|
+
|
777
|
+
def self.decrypt_message(encrypted_message)
|
778
|
+
key = ENV.fetch('MESSAGE_ENCRYPTION_KEY')
|
779
|
+
parsed = JSON.parse(Base64.decode64(encrypted_message))
|
780
|
+
|
781
|
+
cipher = OpenSSL::Cipher.new('AES-256-GCM')
|
782
|
+
cipher.decrypt
|
783
|
+
cipher.key = Base64.decode64(key)
|
784
|
+
cipher.iv = Base64.decode64(parsed['iv'])
|
785
|
+
cipher.auth_tag = Base64.decode64(parsed['tag'])
|
786
|
+
|
787
|
+
cipher.update(Base64.decode64(parsed['data'])) + cipher.final
|
788
|
+
end
|
789
|
+
end
|
790
|
+
```
|
791
|
+
|
792
|
+
## Deployment
|
793
|
+
|
794
|
+
### Docker Configuration
|
795
|
+
|
796
|
+
```dockerfile
|
797
|
+
# Dockerfile
|
798
|
+
FROM ruby:3.2-slim
|
799
|
+
|
800
|
+
# Install system dependencies
|
801
|
+
RUN apt-get update && apt-get install -y \
|
802
|
+
build-essential \
|
803
|
+
libpq-dev \
|
804
|
+
nodejs \
|
805
|
+
&& rm -rf /var/lib/apt/lists/*
|
806
|
+
|
807
|
+
WORKDIR /app
|
808
|
+
|
809
|
+
# Install Ruby dependencies
|
810
|
+
COPY Gemfile Gemfile.lock ./
|
811
|
+
RUN bundle config --global frozen 1 && \
|
812
|
+
bundle install --without development test
|
813
|
+
|
814
|
+
# Copy application code
|
815
|
+
COPY . .
|
816
|
+
|
817
|
+
# Create non-root user
|
818
|
+
RUN adduser --disabled-password --gecos '' appuser && \
|
819
|
+
chown -R appuser:appuser /app
|
820
|
+
USER appuser
|
821
|
+
|
822
|
+
# Health check
|
823
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
824
|
+
CMD curl -f http://localhost:3000/health || exit 1
|
825
|
+
|
826
|
+
EXPOSE 3000
|
827
|
+
|
828
|
+
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
|
829
|
+
```
|
830
|
+
|
831
|
+
### Kubernetes Deployment
|
832
|
+
|
833
|
+
```yaml
|
834
|
+
# k8s/smartmessage-app.yaml
|
835
|
+
apiVersion: apps/v1
|
836
|
+
kind: Deployment
|
837
|
+
metadata:
|
838
|
+
name: smartmessage-app
|
839
|
+
labels:
|
840
|
+
app: smartmessage-app
|
841
|
+
spec:
|
842
|
+
replicas: 3
|
843
|
+
selector:
|
844
|
+
matchLabels:
|
845
|
+
app: smartmessage-app
|
846
|
+
template:
|
847
|
+
metadata:
|
848
|
+
labels:
|
849
|
+
app: smartmessage-app
|
850
|
+
spec:
|
851
|
+
containers:
|
852
|
+
- name: app
|
853
|
+
image: smartmessage-app:latest
|
854
|
+
ports:
|
855
|
+
- containerPort: 3000
|
856
|
+
env:
|
857
|
+
- name: REDIS_URL
|
858
|
+
value: "redis://redis-service:6379"
|
859
|
+
- name: REDIS_PASSWORD
|
860
|
+
valueFrom:
|
861
|
+
secretKeyRef:
|
862
|
+
name: redis-secret
|
863
|
+
key: password
|
864
|
+
resources:
|
865
|
+
requests:
|
866
|
+
memory: "512Mi"
|
867
|
+
cpu: "250m"
|
868
|
+
limits:
|
869
|
+
memory: "1Gi"
|
870
|
+
cpu: "500m"
|
871
|
+
livenessProbe:
|
872
|
+
httpGet:
|
873
|
+
path: /health
|
874
|
+
port: 3000
|
875
|
+
initialDelaySeconds: 30
|
876
|
+
periodSeconds: 10
|
877
|
+
readinessProbe:
|
878
|
+
httpGet:
|
879
|
+
path: /ready
|
880
|
+
port: 3000
|
881
|
+
initialDelaySeconds: 5
|
882
|
+
periodSeconds: 5
|
883
|
+
|
884
|
+
---
|
885
|
+
apiVersion: apps/v1
|
886
|
+
kind: Deployment
|
887
|
+
metadata:
|
888
|
+
name: smartmessage-workers
|
889
|
+
labels:
|
890
|
+
app: smartmessage-workers
|
891
|
+
spec:
|
892
|
+
replicas: 5
|
893
|
+
selector:
|
894
|
+
matchLabels:
|
895
|
+
app: smartmessage-workers
|
896
|
+
template:
|
897
|
+
metadata:
|
898
|
+
labels:
|
899
|
+
app: smartmessage-workers
|
900
|
+
spec:
|
901
|
+
containers:
|
902
|
+
- name: worker
|
903
|
+
image: smartmessage-app:latest
|
904
|
+
command: ["bundle", "exec", "ruby", "lib/workers/start_workers.rb"]
|
905
|
+
env:
|
906
|
+
- name: REDIS_URL
|
907
|
+
value: "redis://redis-service:6379"
|
908
|
+
- name: WORKER_TYPE
|
909
|
+
value: "all"
|
910
|
+
- name: GENERAL_WORKERS
|
911
|
+
value: "2"
|
912
|
+
- name: ORDER_WORKERS
|
913
|
+
value: "1"
|
914
|
+
resources:
|
915
|
+
requests:
|
916
|
+
memory: "256Mi"
|
917
|
+
cpu: "100m"
|
918
|
+
limits:
|
919
|
+
memory: "512Mi"
|
920
|
+
cpu: "200m"
|
921
|
+
```
|
922
|
+
|
923
|
+
### Helm Chart
|
924
|
+
|
925
|
+
```yaml
|
926
|
+
# helm/smartmessage/values.yaml
|
927
|
+
replicaCount: 3
|
928
|
+
|
929
|
+
image:
|
930
|
+
repository: smartmessage
|
931
|
+
tag: latest
|
932
|
+
pullPolicy: IfNotPresent
|
933
|
+
|
934
|
+
redis:
|
935
|
+
host: redis-service
|
936
|
+
port: 6379
|
937
|
+
database: 0
|
938
|
+
auth:
|
939
|
+
enabled: true
|
940
|
+
password: "secure_password"
|
941
|
+
|
942
|
+
workers:
|
943
|
+
enabled: true
|
944
|
+
replicas: 5
|
945
|
+
types:
|
946
|
+
general: 2
|
947
|
+
order: 1
|
948
|
+
payment: 1
|
949
|
+
notification: 1
|
950
|
+
|
951
|
+
resources:
|
952
|
+
app:
|
953
|
+
requests:
|
954
|
+
memory: 512Mi
|
955
|
+
cpu: 250m
|
956
|
+
limits:
|
957
|
+
memory: 1Gi
|
958
|
+
cpu: 500m
|
959
|
+
workers:
|
960
|
+
requests:
|
961
|
+
memory: 256Mi
|
962
|
+
cpu: 100m
|
963
|
+
limits:
|
964
|
+
memory: 512Mi
|
965
|
+
cpu: 200m
|
966
|
+
|
967
|
+
autoscaling:
|
968
|
+
enabled: true
|
969
|
+
minReplicas: 3
|
970
|
+
maxReplicas: 10
|
971
|
+
targetCPUUtilizationPercentage: 70
|
972
|
+
targetMemoryUtilizationPercentage: 80
|
973
|
+
|
974
|
+
monitoring:
|
975
|
+
enabled: true
|
976
|
+
prometheus:
|
977
|
+
enabled: true
|
978
|
+
grafana:
|
979
|
+
enabled: true
|
980
|
+
alerting:
|
981
|
+
enabled: true
|
982
|
+
|
983
|
+
ingress:
|
984
|
+
enabled: true
|
985
|
+
className: nginx
|
986
|
+
annotations:
|
987
|
+
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
988
|
+
hosts:
|
989
|
+
- host: smartmessage.example.com
|
990
|
+
paths:
|
991
|
+
- path: /
|
992
|
+
pathType: Prefix
|
993
|
+
tls:
|
994
|
+
- secretName: smartmessage-tls
|
995
|
+
hosts:
|
996
|
+
- smartmessage.example.com
|
997
|
+
```
|
998
|
+
|
999
|
+
## High Availability
|
1000
|
+
|
1001
|
+
### Redis Sentinel Configuration
|
1002
|
+
|
1003
|
+
```ruby
|
1004
|
+
# config/redis_sentinel.rb
|
1005
|
+
class RedisSentinelConfig
|
1006
|
+
def self.configure_high_availability
|
1007
|
+
SmartMessage.configure do |config|
|
1008
|
+
config.transport_options = {
|
1009
|
+
# Sentinel configuration
|
1010
|
+
sentinels: [
|
1011
|
+
{ host: 'sentinel-1.internal', port: 26379 },
|
1012
|
+
{ host: 'sentinel-2.internal', port: 26379 },
|
1013
|
+
{ host: 'sentinel-3.internal', port: 26379 }
|
1014
|
+
],
|
1015
|
+
name: 'smartmessage-redis',
|
1016
|
+
password: ENV['REDIS_PASSWORD'],
|
1017
|
+
|
1018
|
+
# Failover settings
|
1019
|
+
sentinel_timeout: 5,
|
1020
|
+
connect_timeout: 5,
|
1021
|
+
read_timeout: 5,
|
1022
|
+
write_timeout: 5,
|
1023
|
+
|
1024
|
+
# Retry configuration
|
1025
|
+
reconnect_attempts: 5,
|
1026
|
+
reconnect_delay: 2,
|
1027
|
+
|
1028
|
+
# Connection pool for HA
|
1029
|
+
pool_size: 20,
|
1030
|
+
pool_timeout: 10
|
1031
|
+
}
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
RedisSentinelConfig.configure_high_availability if Rails.env.production?
|
1037
|
+
```
|
1038
|
+
|
1039
|
+
### Disaster Recovery
|
1040
|
+
|
1041
|
+
```ruby
|
1042
|
+
# lib/disaster_recovery.rb
|
1043
|
+
module DisasterRecovery
|
1044
|
+
class BackupManager
|
1045
|
+
def self.setup_automated_backups
|
1046
|
+
# Daily Redis backup
|
1047
|
+
whenever_schedule = <<~SCHEDULE
|
1048
|
+
# Redis backup every day at 2 AM
|
1049
|
+
0 2 * * * cd #{Rails.root} && bundle exec ruby lib/disaster_recovery/backup_redis.rb
|
1050
|
+
|
1051
|
+
# Weekly full backup
|
1052
|
+
0 1 * * 0 cd #{Rails.root} && bundle exec ruby lib/disaster_recovery/full_backup.rb
|
1053
|
+
SCHEDULE
|
1054
|
+
|
1055
|
+
File.write('config/schedule.rb', whenever_schedule)
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
def self.backup_redis_data
|
1059
|
+
timestamp = Time.current.strftime('%Y%m%d_%H%M%S')
|
1060
|
+
backup_file = "backups/redis_backup_#{timestamp}.rdb"
|
1061
|
+
|
1062
|
+
system("redis-cli --rdb #{backup_file}")
|
1063
|
+
|
1064
|
+
# Upload to S3 or other backup storage
|
1065
|
+
if ENV['AWS_S3_BACKUP_BUCKET']
|
1066
|
+
upload_to_s3(backup_file)
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
Rails.logger.info "Redis backup completed: #{backup_file}"
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
def self.restore_from_backup(backup_file)
|
1073
|
+
Rails.logger.info "Starting Redis restore from: #{backup_file}"
|
1074
|
+
|
1075
|
+
# Stop Redis
|
1076
|
+
system('sudo systemctl stop redis')
|
1077
|
+
|
1078
|
+
# Replace RDB file
|
1079
|
+
system("sudo cp #{backup_file} /var/lib/redis/dump.rdb")
|
1080
|
+
system('sudo chown redis:redis /var/lib/redis/dump.rdb')
|
1081
|
+
|
1082
|
+
# Start Redis
|
1083
|
+
system('sudo systemctl start redis')
|
1084
|
+
|
1085
|
+
Rails.logger.info "Redis restore completed"
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
This production guide provides the foundation for deploying Redis Queue Transport in enterprise environments. Adapt the configurations based on your specific infrastructure and requirements, always testing thoroughly in staging environments before production deployment.
|