smart_message 0.0.13 → 0.0.16
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/.gitignore +1 -0
- data/CHANGELOG.md +120 -0
- data/Gemfile.lock +3 -3
- data/README.md +71 -25
- data/docs/index.md +2 -0
- data/docs/reference/transports.md +46 -21
- data/docs/transports/memory-transport.md +2 -1
- data/docs/transports/multi-transport.md +484 -0
- data/examples/file/00_run_all_file_demos.rb +260 -0
- data/examples/file/01_basic_file_transport_demo.rb +237 -0
- data/examples/file/02_fifo_transport_demo.rb +289 -0
- data/examples/file/03_file_watching_demo.rb +332 -0
- data/examples/file/04_multi_transport_file_demo.rb +432 -0
- data/examples/file/README.md +257 -0
- data/examples/memory/00_run_all_demos.rb +317 -0
- data/examples/memory/01_message_deduplication_demo.rb +18 -30
- data/examples/memory/02_dead_letter_queue_demo.rb +9 -9
- data/examples/memory/03_point_to_point_orders.rb +3 -3
- data/examples/memory/04_publish_subscribe_events.rb +15 -15
- data/examples/memory/05_many_to_many_chat.rb +19 -19
- data/examples/memory/06_stdout_publish_only.rb +145 -0
- data/examples/memory/07_proc_handlers_demo.rb +13 -13
- data/examples/memory/08_custom_logger_demo.rb +136 -136
- data/examples/memory/09_error_handling_demo.rb +7 -7
- data/examples/memory/10_entity_addressing_basic.rb +25 -25
- data/examples/memory/11_entity_addressing_with_filtering.rb +32 -32
- data/examples/memory/12_regex_filtering_microservices.rb +10 -10
- data/examples/memory/14_global_configuration_demo.rb +12 -12
- data/examples/memory/README.md +34 -17
- data/examples/memory/log/demo_app.log.1 +100 -0
- data/examples/memory/log/demo_app.log.2 +100 -0
- data/examples/multi_transport_example.rb +114 -0
- data/examples/redis/01_smart_home_iot_demo.rb +20 -20
- data/examples/utilities/box_it.rb +12 -0
- data/examples/utilities/doing.rb +19 -0
- data/examples/utilities/temp.md +28 -0
- data/lib/smart_message/base.rb +5 -7
- data/lib/smart_message/errors.rb +3 -0
- data/lib/smart_message/header.rb +1 -1
- data/lib/smart_message/logger/default.rb +1 -1
- data/lib/smart_message/messaging.rb +36 -6
- data/lib/smart_message/plugins.rb +46 -4
- data/lib/smart_message/serializer/base.rb +1 -1
- data/lib/smart_message/serializer.rb +3 -2
- data/lib/smart_message/subscription.rb +18 -20
- data/lib/smart_message/transport/async_publish_queue.rb +284 -0
- data/lib/smart_message/transport/fifo_operations.rb +264 -0
- data/lib/smart_message/transport/file_operations.rb +200 -0
- data/lib/smart_message/transport/file_transport.rb +149 -0
- data/lib/smart_message/transport/file_watching.rb +72 -0
- data/lib/smart_message/transport/partitioned_files.rb +46 -0
- data/lib/smart_message/transport/stdout_transport.rb +50 -36
- data/lib/smart_message/transport/stdout_transport.rb.backup +88 -0
- data/lib/smart_message/version.rb +1 -1
- metadata +24 -10
- data/ideas/README.md +0 -41
- data/ideas/agents.md +0 -1001
- data/ideas/database_transport.md +0 -980
- data/ideas/improvement.md +0 -359
- data/ideas/meshage.md +0 -1788
- data/ideas/message_discovery.md +0 -178
- data/ideas/message_schema.md +0 -1381
- data/lib/smart_message/wrapper.rb.bak +0 -132
- /data/examples/memory/{06_pretty_print_demo.rb → 16_pretty_print_demo.rb} +0 -0
@@ -0,0 +1,484 @@
|
|
1
|
+
# Multi-Transport Publishing
|
2
|
+
|
3
|
+
**Send messages to multiple transports simultaneously for redundancy, integration, and migration scenarios.**
|
4
|
+
|
5
|
+
SmartMessage supports configuring messages with multiple transports, enabling sophisticated messaging patterns where a single `publish()` operation can deliver messages across different transport systems simultaneously.
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
|
9
|
+
Multi-transport publishing allows you to:
|
10
|
+
|
11
|
+
- **Redundancy**: Send critical messages to primary and backup systems
|
12
|
+
- **Integration**: Simultaneously deliver to production queues and logging/monitoring systems
|
13
|
+
- **Migration**: Gradually transition between transport systems without downtime
|
14
|
+
- **Fan-out**: Broadcast messages to multiple processing pipelines
|
15
|
+
- **Resilience**: Ensure message delivery succeeds as long as ANY transport is available
|
16
|
+
|
17
|
+
## Basic Configuration
|
18
|
+
|
19
|
+
Configure multiple transports by passing an array to the `transport` method:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class OrderProcessingMessage < SmartMessage::Base
|
23
|
+
property :order_id, required: true
|
24
|
+
property :customer_id, required: true
|
25
|
+
property :amount, required: true
|
26
|
+
|
27
|
+
# Configure multiple transports
|
28
|
+
transport [
|
29
|
+
SmartMessage::Transport.create(:redis_queue, url: 'redis://primary:6379'),
|
30
|
+
SmartMessage::Transport.create(:redis, url: 'redis://backup:6379'),
|
31
|
+
SmartMessage::Transport::StdoutTransport.new(format: :json)
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Publishing sends to ALL configured transports
|
36
|
+
message = OrderProcessingMessage.new(
|
37
|
+
order_id: "ORD-12345",
|
38
|
+
customer_id: "CUST-789",
|
39
|
+
amount: 149.99
|
40
|
+
)
|
41
|
+
|
42
|
+
message.publish # ✅ Publishes to Redis Queue, Redis Pub/Sub, and STDOUT
|
43
|
+
```
|
44
|
+
|
45
|
+
## Transport Introspection
|
46
|
+
|
47
|
+
SmartMessage provides utility methods to inspect and manage transport configurations:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Check transport configuration
|
51
|
+
puts message.multiple_transports? # => true
|
52
|
+
puts message.single_transport? # => false
|
53
|
+
puts message.transports.length # => 3
|
54
|
+
|
55
|
+
# Access individual transports
|
56
|
+
message.transports.each_with_index do |transport, index|
|
57
|
+
puts "Transport #{index}: #{transport.class.name}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get primary transport (first in array) for backward compatibility
|
61
|
+
primary = message.transport # Returns first transport
|
62
|
+
```
|
63
|
+
|
64
|
+
## Instance-Level Overrides
|
65
|
+
|
66
|
+
You can override class-level multi-transport configuration at the instance level:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class MonitoringMessage < SmartMessage::Base
|
70
|
+
property :metric, required: true
|
71
|
+
|
72
|
+
# Class-level: send to monitoring and backup
|
73
|
+
transport [
|
74
|
+
SmartMessage::Transport.create(:redis, url: 'redis://monitoring:6379'),
|
75
|
+
SmartMessage::Transport.create(:redis, url: 'redis://backup:6379')
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Instance-level override for testing
|
80
|
+
test_message = MonitoringMessage.new(metric: "cpu_usage: 85%")
|
81
|
+
test_message.transport(SmartMessage::Transport::StdoutTransport.new)
|
82
|
+
|
83
|
+
puts test_message.single_transport? # => true (overridden)
|
84
|
+
test_message.publish # Only goes to STDOUT
|
85
|
+
```
|
86
|
+
|
87
|
+
## Error Handling and Resilience
|
88
|
+
|
89
|
+
Multi-transport publishing is designed to be resilient:
|
90
|
+
|
91
|
+
### Partial Failures
|
92
|
+
|
93
|
+
When some transports succeed and others fail, publishing continues:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class CriticalAlert < SmartMessage::Base
|
97
|
+
property :alert_text, required: true
|
98
|
+
|
99
|
+
transport [
|
100
|
+
ReliableTransport.new, # ✅ Succeeds
|
101
|
+
FailingTransport.new, # ❌ Fails
|
102
|
+
BackupTransport.new # ✅ Succeeds
|
103
|
+
]
|
104
|
+
end
|
105
|
+
|
106
|
+
alert = CriticalAlert.new(alert_text: "Database connection lost")
|
107
|
+
alert.publish # ✅ Succeeds! 2 out of 3 transports work
|
108
|
+
|
109
|
+
# Logs will show:
|
110
|
+
# [INFO] Published: CriticalAlert via ReliableTransport, BackupTransport
|
111
|
+
# [WARN] Failed transports for CriticalAlert: FailingTransport
|
112
|
+
```
|
113
|
+
|
114
|
+
### Complete Failures
|
115
|
+
|
116
|
+
Only when ALL transports fail does publishing raise an error:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
class AllFailingMessage < SmartMessage::Base
|
120
|
+
property :data
|
121
|
+
|
122
|
+
transport [
|
123
|
+
FailingTransport.new, # ❌ Fails
|
124
|
+
AnotherFailingTransport.new # ❌ Fails
|
125
|
+
]
|
126
|
+
end
|
127
|
+
|
128
|
+
message = AllFailingMessage.new(data: "test")
|
129
|
+
|
130
|
+
begin
|
131
|
+
message.publish
|
132
|
+
rescue SmartMessage::Errors::PublishError => e
|
133
|
+
puts e.message # "All transports failed: FailingTransport: connection error; AnotherFailingTransport: timeout"
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
### Error Logging
|
138
|
+
|
139
|
+
Multi-transport publishing provides comprehensive error logging:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# Example log output during partial failure:
|
143
|
+
[DEBUG] About to call transport.publish on RedisTransport
|
144
|
+
[DEBUG] transport.publish completed on RedisTransport
|
145
|
+
[ERROR] Transport FailingTransport failed: StandardError - Connection timeout
|
146
|
+
[DEBUG] About to call transport.publish on StdoutTransport
|
147
|
+
[DEBUG] transport.publish completed on StdoutTransport
|
148
|
+
[INFO] Published: MyMessage via RedisTransport, StdoutTransport
|
149
|
+
[WARN] Failed transports for MyMessage: FailingTransport
|
150
|
+
```
|
151
|
+
|
152
|
+
## Common Use Cases
|
153
|
+
|
154
|
+
### 1. High-Availability Critical Messages
|
155
|
+
|
156
|
+
Ensure critical business messages reach their destination even if primary systems fail:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class PaymentProcessedMessage < SmartMessage::Base
|
160
|
+
property :payment_id, required: true
|
161
|
+
property :amount, required: true
|
162
|
+
property :status, required: true
|
163
|
+
|
164
|
+
# Primary processing + backup + audit trail
|
165
|
+
transport [
|
166
|
+
SmartMessage::Transport.create(:redis_queue,
|
167
|
+
url: 'redis://primary-cluster:6379',
|
168
|
+
queue_prefix: 'payments'
|
169
|
+
),
|
170
|
+
SmartMessage::Transport.create(:redis,
|
171
|
+
url: 'redis://backup-cluster:6380'
|
172
|
+
),
|
173
|
+
SmartMessage::Transport::StdoutTransport.new(
|
174
|
+
output: '/var/log/payments.log',
|
175
|
+
format: :json
|
176
|
+
)
|
177
|
+
]
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
### 2. Development and Production Dual Publishing
|
182
|
+
|
183
|
+
Send messages to both production and development environments during migration:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
class UserRegistrationMessage < SmartMessage::Base
|
187
|
+
property :user_id, required: true
|
188
|
+
property :email, required: true
|
189
|
+
|
190
|
+
# Dual publishing during migration
|
191
|
+
transport [
|
192
|
+
SmartMessage::Transport.create(:redis,
|
193
|
+
url: ENV['PRODUCTION_REDIS_URL']
|
194
|
+
),
|
195
|
+
SmartMessage::Transport.create(:redis_queue,
|
196
|
+
url: ENV['NEW_SYSTEM_REDIS_URL'],
|
197
|
+
queue_prefix: 'migration'
|
198
|
+
)
|
199
|
+
]
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
### 3. Monitoring and Alerting Integration
|
204
|
+
|
205
|
+
Combine business processing with operational monitoring:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
class OrderFailureMessage < SmartMessage::Base
|
209
|
+
property :order_id, required: true
|
210
|
+
property :error_message, required: true
|
211
|
+
property :customer_impact, required: true
|
212
|
+
|
213
|
+
transport [
|
214
|
+
# Business processing
|
215
|
+
SmartMessage::Transport.create(:redis_queue,
|
216
|
+
url: 'redis://orders:6379'
|
217
|
+
),
|
218
|
+
|
219
|
+
# Operations monitoring
|
220
|
+
SmartMessage::Transport.create(:webhook,
|
221
|
+
url: 'https://monitoring.company.com/alerts'
|
222
|
+
),
|
223
|
+
|
224
|
+
# Development debugging
|
225
|
+
SmartMessage::Transport::StdoutTransport.new(format: :pretty)
|
226
|
+
]
|
227
|
+
end
|
228
|
+
```
|
229
|
+
|
230
|
+
### 4. A/B Testing and Feature Rollouts
|
231
|
+
|
232
|
+
Send messages to old and new systems during feature rollouts:
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
class AnalyticsEventMessage < SmartMessage::Base
|
236
|
+
property :event_type, required: true
|
237
|
+
property :user_id, required: true
|
238
|
+
property :metadata, default: {}
|
239
|
+
|
240
|
+
transport [
|
241
|
+
# Existing analytics pipeline (stable)
|
242
|
+
SmartMessage::Transport.create(:redis,
|
243
|
+
url: 'redis://analytics-v1:6379'
|
244
|
+
),
|
245
|
+
|
246
|
+
# New analytics pipeline (testing)
|
247
|
+
SmartMessage::Transport.create(:redis_queue,
|
248
|
+
url: 'redis://analytics-v2:6379',
|
249
|
+
queue_prefix: 'beta'
|
250
|
+
)
|
251
|
+
]
|
252
|
+
end
|
253
|
+
```
|
254
|
+
|
255
|
+
## Performance Considerations
|
256
|
+
|
257
|
+
### Sequential Processing
|
258
|
+
|
259
|
+
Transports are processed sequentially in the order configured:
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
# Order matters for performance
|
263
|
+
transport [
|
264
|
+
FastMemoryTransport.new, # Processed first (fast)
|
265
|
+
SlowNetworkTransport.new, # Processed second (slow)
|
266
|
+
AnotherFastTransport.new # Processed third (waits for slow)
|
267
|
+
]
|
268
|
+
```
|
269
|
+
|
270
|
+
**Recommendation**: Place fastest/most critical transports first.
|
271
|
+
|
272
|
+
### Transport Independence
|
273
|
+
|
274
|
+
Each transport failure is isolated and doesn't affect others:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
transport [
|
278
|
+
ReliableTransport.new, # Always succeeds
|
279
|
+
UnreliableTransport.new, # May fail, doesn't affect others
|
280
|
+
BackupTransport.new # Provides redundancy
|
281
|
+
]
|
282
|
+
```
|
283
|
+
|
284
|
+
### Memory Usage
|
285
|
+
|
286
|
+
Each transport instance maintains its own connection and state:
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
# Each transport creates its own connection pool
|
290
|
+
transport [
|
291
|
+
SmartMessage::Transport.create(:redis, url: 'redis://server1:6379'),
|
292
|
+
SmartMessage::Transport.create(:redis, url: 'redis://server2:6379'),
|
293
|
+
SmartMessage::Transport.create(:redis, url: 'redis://server3:6379')
|
294
|
+
]
|
295
|
+
# Total: 3 Redis connection pools
|
296
|
+
```
|
297
|
+
|
298
|
+
## Best Practices
|
299
|
+
|
300
|
+
### 1. Limit Transport Count
|
301
|
+
|
302
|
+
Don't configure excessive transports as this impacts performance:
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
# ✅ Good: 2-4 transports for specific purposes
|
306
|
+
transport [
|
307
|
+
PrimaryTransport.new,
|
308
|
+
BackupTransport.new,
|
309
|
+
MonitoringTransport.new
|
310
|
+
]
|
311
|
+
|
312
|
+
# ❌ Avoid: Too many transports
|
313
|
+
transport [
|
314
|
+
Transport1.new, Transport2.new, Transport3.new,
|
315
|
+
Transport4.new, Transport5.new, Transport6.new # Overkill
|
316
|
+
]
|
317
|
+
```
|
318
|
+
|
319
|
+
### 2. Group by Purpose
|
320
|
+
|
321
|
+
Organize transports by their intended purpose:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
class BusinessMessage < SmartMessage::Base
|
325
|
+
transport [
|
326
|
+
# Core business processing
|
327
|
+
SmartMessage::Transport.create(:redis_queue, url: primary_redis_url),
|
328
|
+
|
329
|
+
# Operational monitoring
|
330
|
+
SmartMessage::Transport::StdoutTransport.new(
|
331
|
+
output: '/var/log/business-events.log'
|
332
|
+
),
|
333
|
+
|
334
|
+
# Disaster recovery backup
|
335
|
+
SmartMessage::Transport.create(:redis, url: backup_redis_url)
|
336
|
+
]
|
337
|
+
end
|
338
|
+
```
|
339
|
+
|
340
|
+
### 3. Environment-Specific Configuration
|
341
|
+
|
342
|
+
Use environment variables for transport configuration:
|
343
|
+
|
344
|
+
```ruby
|
345
|
+
class ConfigurableMessage < SmartMessage::Base
|
346
|
+
transport_configs = []
|
347
|
+
|
348
|
+
# Always include primary transport
|
349
|
+
transport_configs << SmartMessage::Transport.create(:redis_queue,
|
350
|
+
url: ENV['PRIMARY_REDIS_URL']
|
351
|
+
)
|
352
|
+
|
353
|
+
# Add backup transport in production
|
354
|
+
if Rails.env.production?
|
355
|
+
transport_configs << SmartMessage::Transport.create(:redis,
|
356
|
+
url: ENV['BACKUP_REDIS_URL']
|
357
|
+
)
|
358
|
+
end
|
359
|
+
|
360
|
+
# Add stdout transport in development
|
361
|
+
if Rails.env.development?
|
362
|
+
transport_configs << SmartMessage::Transport::StdoutTransport.new
|
363
|
+
end
|
364
|
+
|
365
|
+
transport transport_configs
|
366
|
+
end
|
367
|
+
```
|
368
|
+
|
369
|
+
### 4. Health Monitoring
|
370
|
+
|
371
|
+
Monitor the health of your multi-transport setup:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
class HealthCheckMessage < SmartMessage::Base
|
375
|
+
property :timestamp, default: -> { Time.now }
|
376
|
+
|
377
|
+
transport [
|
378
|
+
PrimaryTransport.new,
|
379
|
+
BackupTransport.new
|
380
|
+
]
|
381
|
+
|
382
|
+
# Class method to check transport health
|
383
|
+
def self.health_check
|
384
|
+
test_message = new(timestamp: Time.now)
|
385
|
+
|
386
|
+
begin
|
387
|
+
test_message.publish
|
388
|
+
{ status: 'healthy', transports: 'all_operational' }
|
389
|
+
rescue SmartMessage::Errors::PublishError => e
|
390
|
+
{ status: 'degraded', error: e.message }
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
```
|
395
|
+
|
396
|
+
## Migration Strategies
|
397
|
+
|
398
|
+
### Gradual Migration
|
399
|
+
|
400
|
+
When migrating from one transport to another:
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
class MigrationMessage < SmartMessage::Base
|
404
|
+
|
405
|
+
# Phase 1: Dual publishing
|
406
|
+
transport [
|
407
|
+
OldTransport.new, # Keep existing system running
|
408
|
+
NewTransport.new # Start sending to new system
|
409
|
+
]
|
410
|
+
|
411
|
+
# Phase 2: Monitor and validate new system
|
412
|
+
# Phase 3: Remove old transport when confident
|
413
|
+
end
|
414
|
+
```
|
415
|
+
|
416
|
+
### Blue-Green Deployment
|
417
|
+
|
418
|
+
Support blue-green deployments with transport switching:
|
419
|
+
|
420
|
+
```ruby
|
421
|
+
class DeploymentMessage < SmartMessage::Base
|
422
|
+
def self.configure_for_deployment(color)
|
423
|
+
case color
|
424
|
+
when :blue
|
425
|
+
transport BlueEnvironmentTransport.new
|
426
|
+
when :green
|
427
|
+
transport GreenEnvironmentTransport.new
|
428
|
+
when :both
|
429
|
+
transport [
|
430
|
+
BlueEnvironmentTransport.new,
|
431
|
+
GreenEnvironmentTransport.new
|
432
|
+
]
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
```
|
437
|
+
|
438
|
+
## Troubleshooting
|
439
|
+
|
440
|
+
### Common Issues
|
441
|
+
|
442
|
+
**Issue**: Publishing seems slow
|
443
|
+
```ruby
|
444
|
+
# Check transport order - slow transports block subsequent ones
|
445
|
+
transport [
|
446
|
+
SlowTransport.new, # ❌ Blocks others
|
447
|
+
FastTransport.new # Must wait for slow one
|
448
|
+
]
|
449
|
+
|
450
|
+
# Solution: Reorder with fastest first
|
451
|
+
transport [
|
452
|
+
FastTransport.new, # ✅ Completes quickly
|
453
|
+
SlowTransport.new # Others don't wait
|
454
|
+
]
|
455
|
+
```
|
456
|
+
|
457
|
+
**Issue**: Partial failures not logged
|
458
|
+
```ruby
|
459
|
+
# Ensure proper logging configuration
|
460
|
+
SmartMessage.configure do |config|
|
461
|
+
config.logger.level = :debug # Show all transport operations
|
462
|
+
end
|
463
|
+
```
|
464
|
+
|
465
|
+
**Issue**: All transports failing unexpectedly
|
466
|
+
```ruby
|
467
|
+
# Test each transport individually
|
468
|
+
message.transports.each_with_index do |transport, index|
|
469
|
+
begin
|
470
|
+
transport.publish(message)
|
471
|
+
puts "Transport #{index} (#{transport.class.name}): ✅ Success"
|
472
|
+
rescue => e
|
473
|
+
puts "Transport #{index} (#{transport.class.name}): ❌ Failed - #{e.message}"
|
474
|
+
end
|
475
|
+
end
|
476
|
+
```
|
477
|
+
|
478
|
+
## See Also
|
479
|
+
|
480
|
+
- [Transport Layer Overview](../reference/transports.md)
|
481
|
+
- [Redis Queue Transport](redis-transport.md)
|
482
|
+
- [Memory Transport](memory-transport.md)
|
483
|
+
- [Error Handling and Dead Letter Queues](../reference/dead-letter-queue.md)
|
484
|
+
- [Performance Optimization](../development/performance.md)
|