smart_message 0.0.8 → 0.0.10
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/.irbrc +24 -0
- data/CHANGELOG.md +119 -0
- data/Gemfile.lock +6 -1
- data/README.md +389 -17
- data/docs/README.md +3 -1
- data/docs/addressing.md +119 -13
- data/docs/architecture.md +184 -46
- data/docs/dead_letter_queue.md +673 -0
- data/docs/dispatcher.md +87 -0
- data/docs/examples.md +59 -1
- data/docs/getting-started.md +8 -1
- data/docs/logging.md +382 -326
- data/docs/message_deduplication.md +488 -0
- data/docs/message_filtering.md +451 -0
- data/examples/01_point_to_point_orders.rb +54 -53
- data/examples/02_publish_subscribe_events.rb +14 -10
- data/examples/03_many_to_many_chat.rb +16 -8
- data/examples/04_redis_smart_home_iot.rb +20 -10
- data/examples/05_proc_handlers.rb +12 -11
- data/examples/06_custom_logger_example.rb +95 -100
- data/examples/07_error_handling_scenarios.rb +4 -2
- data/examples/08_entity_addressing_basic.rb +18 -6
- data/examples/08_entity_addressing_with_filtering.rb +27 -9
- data/examples/09_dead_letter_queue_demo.rb +559 -0
- data/examples/09_regex_filtering_microservices.rb +407 -0
- data/examples/10_header_block_configuration.rb +263 -0
- data/examples/10_message_deduplication.rb +209 -0
- data/examples/11_global_configuration_example.rb +219 -0
- data/examples/README.md +102 -0
- data/examples/dead_letters.jsonl +12 -0
- data/examples/performance_metrics/benchmark_results_ractor_20250818_205603.json +135 -0
- data/examples/performance_metrics/benchmark_results_ractor_20250818_205831.json +135 -0
- data/examples/performance_metrics/benchmark_results_test_20250818_204942.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json +130 -0
- data/examples/performance_metrics/benchmark_results_unknown_20250819_172852.json +130 -0
- data/examples/performance_metrics/compare_benchmarks.rb +519 -0
- data/examples/performance_metrics/dead_letters.jsonl +3100 -0
- data/examples/performance_metrics/performance_benchmark.rb +344 -0
- data/examples/show_logger.rb +367 -0
- data/examples/show_me.rb +145 -0
- data/examples/temp.txt +94 -0
- data/examples/tmux_chat/bot_agent.rb +4 -2
- data/examples/tmux_chat/human_agent.rb +4 -2
- data/examples/tmux_chat/room_monitor.rb +4 -2
- data/examples/tmux_chat/shared_chat_system.rb +6 -3
- data/lib/smart_message/addressing.rb +259 -0
- data/lib/smart_message/base.rb +123 -599
- data/lib/smart_message/circuit_breaker.rb +2 -1
- data/lib/smart_message/configuration.rb +199 -0
- data/lib/smart_message/ddq/base.rb +71 -0
- data/lib/smart_message/ddq/memory.rb +109 -0
- data/lib/smart_message/ddq/redis.rb +168 -0
- data/lib/smart_message/ddq.rb +31 -0
- data/lib/smart_message/dead_letter_queue.rb +27 -10
- data/lib/smart_message/deduplication.rb +174 -0
- data/lib/smart_message/dispatcher.rb +259 -61
- data/lib/smart_message/header.rb +5 -0
- data/lib/smart_message/logger/base.rb +21 -1
- data/lib/smart_message/logger/default.rb +88 -138
- data/lib/smart_message/logger/lumberjack.rb +324 -0
- data/lib/smart_message/logger/null.rb +81 -0
- data/lib/smart_message/logger.rb +17 -9
- data/lib/smart_message/messaging.rb +100 -0
- data/lib/smart_message/plugins.rb +132 -0
- data/lib/smart_message/serializer/base.rb +25 -8
- data/lib/smart_message/serializer/json.rb +5 -4
- data/lib/smart_message/subscription.rb +196 -0
- data/lib/smart_message/transport/base.rb +72 -41
- data/lib/smart_message/transport/memory_transport.rb +7 -5
- data/lib/smart_message/transport/redis_transport.rb +15 -45
- data/lib/smart_message/transport/stdout_transport.rb +18 -8
- data/lib/smart_message/transport.rb +1 -34
- data/lib/smart_message/utilities.rb +142 -0
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message/versioning.rb +85 -0
- data/lib/smart_message/wrapper.rb.bak +132 -0
- data/lib/smart_message.rb +74 -28
- data/smart_message.gemspec +3 -0
- metadata +83 -3
- data/lib/smart_message/serializer.rb +0 -10
- data/lib/smart_message/wrapper.rb +0 -43
data/docs/README.md
CHANGED
@@ -17,11 +17,13 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
17
17
|
- [SmartMessage::Base](base.md)
|
18
18
|
- [Property System](properties.md)
|
19
19
|
- [Entity Addressing](addressing.md)
|
20
|
+
- [Message Filtering](message_filtering.md)
|
20
21
|
- [Transport Layer](transports.md)
|
21
22
|
- [Serializers](serializers.md)
|
22
23
|
- [Logging System](logging.md)
|
23
24
|
- [Dispatcher & Routing](dispatcher.md)
|
24
25
|
- [Message Headers](headers.md)
|
26
|
+
- [Dead Letter Queue](dead_letter_queue.md)
|
25
27
|
|
26
28
|
### Advanced Topics
|
27
29
|
- [Custom Transports](custom-transports.md)
|
@@ -50,6 +52,6 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
50
52
|
|
51
53
|
## Version
|
52
54
|
|
53
|
-
This documentation is for SmartMessage v0.0.
|
55
|
+
This documentation is for SmartMessage v0.0.8.
|
54
56
|
|
55
57
|
For older versions, please check the git tags and corresponding documentation.
|
data/docs/addressing.md
CHANGED
@@ -63,13 +63,14 @@ end
|
|
63
63
|
|
64
64
|
### Class-Level Configuration
|
65
65
|
|
66
|
-
Set default addressing for all instances of a message class:
|
66
|
+
Set default addressing for all instances of a message class using three different approaches:
|
67
67
|
|
68
|
+
#### Method 1: Direct Class Methods
|
68
69
|
```ruby
|
69
70
|
class PaymentMessage < SmartMessage::Base
|
70
71
|
version 1
|
71
72
|
|
72
|
-
# Class-level addressing
|
73
|
+
# Class-level addressing using direct methods
|
73
74
|
from 'payment-service'
|
74
75
|
to 'bank-gateway'
|
75
76
|
reply_to 'payment-service'
|
@@ -77,6 +78,42 @@ class PaymentMessage < SmartMessage::Base
|
|
77
78
|
property :amount, required: true
|
78
79
|
property :account_id, required: true
|
79
80
|
end
|
81
|
+
```
|
82
|
+
|
83
|
+
#### Method 2: Header Block DSL
|
84
|
+
```ruby
|
85
|
+
class PaymentMessage < SmartMessage::Base
|
86
|
+
version 1
|
87
|
+
|
88
|
+
# Class-level addressing using header block
|
89
|
+
header do
|
90
|
+
from 'payment-service'
|
91
|
+
to 'bank-gateway'
|
92
|
+
reply_to 'payment-service'
|
93
|
+
end
|
94
|
+
|
95
|
+
property :amount, required: true
|
96
|
+
property :account_id, required: true
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
#### Method 3: Mixed Approach
|
101
|
+
```ruby
|
102
|
+
class PaymentMessage < SmartMessage::Base
|
103
|
+
version 1
|
104
|
+
|
105
|
+
# You can mix approaches if needed
|
106
|
+
header do
|
107
|
+
from 'payment-service'
|
108
|
+
to 'bank-gateway'
|
109
|
+
end
|
110
|
+
|
111
|
+
# Additional addressing outside the block
|
112
|
+
reply_to 'payment-service'
|
113
|
+
|
114
|
+
property :amount, required: true
|
115
|
+
property :account_id, required: true
|
116
|
+
end
|
80
117
|
|
81
118
|
# All instances inherit class addressing
|
82
119
|
payment = PaymentMessage.new(amount: 100.00, account_id: "ACCT-123")
|
@@ -87,27 +124,54 @@ puts payment._sm_header.reply_to # => 'payment-service'
|
|
87
124
|
|
88
125
|
### Instance-Level Overrides
|
89
126
|
|
90
|
-
Override addressing for specific message instances:
|
127
|
+
Override addressing for specific message instances using multiple approaches:
|
91
128
|
|
129
|
+
#### Method Chaining
|
92
130
|
```ruby
|
93
131
|
class FlexibleMessage < SmartMessage::Base
|
94
|
-
|
95
|
-
|
132
|
+
header do
|
133
|
+
from 'service-a'
|
134
|
+
to 'service-b'
|
135
|
+
end
|
96
136
|
|
97
137
|
property :data
|
98
138
|
end
|
99
139
|
|
100
|
-
# Override addressing
|
140
|
+
# Override addressing using method chaining
|
101
141
|
message = FlexibleMessage.new(data: "test")
|
102
142
|
message.from('different-sender')
|
103
|
-
|
104
|
-
|
143
|
+
.to('different-recipient')
|
144
|
+
.reply_to('different-reply-service')
|
105
145
|
|
106
146
|
puts message.from # => 'different-sender'
|
107
147
|
puts message.to # => 'different-recipient'
|
108
148
|
puts message.reply_to # => 'different-reply-service'
|
149
|
+
```
|
150
|
+
|
151
|
+
#### Setter Methods
|
152
|
+
```ruby
|
153
|
+
# Override addressing using setter syntax
|
154
|
+
message = FlexibleMessage.new(data: "test")
|
155
|
+
message.from = 'different-sender'
|
156
|
+
message.to = 'different-recipient'
|
157
|
+
message.reply_to = 'different-reply-service'
|
158
|
+
```
|
109
159
|
|
110
|
-
|
160
|
+
#### Accessing Addressing Values
|
161
|
+
```ruby
|
162
|
+
# Three ways to access addressing values
|
163
|
+
|
164
|
+
# 1. Direct shortcut methods
|
165
|
+
puts message.from # => 'different-sender'
|
166
|
+
puts message.to # => 'different-recipient'
|
167
|
+
puts message.reply_to # => 'different-reply-service'
|
168
|
+
|
169
|
+
# 2. Via header object
|
170
|
+
puts message._sm_header.from # => 'different-sender'
|
171
|
+
puts message._sm_header.to # => 'different-recipient'
|
172
|
+
puts message._sm_header.reply_to # => 'different-reply-service'
|
173
|
+
|
174
|
+
# 3. Class defaults remain unchanged
|
111
175
|
puts FlexibleMessage.from # => 'service-a'
|
112
176
|
puts FlexibleMessage.to # => 'service-b'
|
113
177
|
```
|
@@ -257,27 +321,69 @@ message.publish # Raises SmartMessage::Errors::ValidationError
|
|
257
321
|
# => "The property 'from' From entity ID is required for message routing and replies"
|
258
322
|
```
|
259
323
|
|
324
|
+
### Checking Address Configuration
|
325
|
+
```ruby
|
326
|
+
class ValidatedMessage < SmartMessage::Base
|
327
|
+
header do
|
328
|
+
from 'my-service'
|
329
|
+
to 'target-service'
|
330
|
+
end
|
331
|
+
|
332
|
+
property :data
|
333
|
+
end
|
334
|
+
|
335
|
+
# Class-level checks
|
336
|
+
puts ValidatedMessage.from_configured? # => true
|
337
|
+
puts ValidatedMessage.to_configured? # => true
|
338
|
+
puts ValidatedMessage.reply_to_configured? # => false
|
339
|
+
puts ValidatedMessage.reply_to_missing? # => true
|
340
|
+
|
341
|
+
# Instance-level checks
|
342
|
+
message = ValidatedMessage.new(data: "test")
|
343
|
+
puts message.from_configured? # => true
|
344
|
+
puts message.to_missing? # => false
|
345
|
+
|
346
|
+
# Reset addressing
|
347
|
+
message.reset_from
|
348
|
+
puts message.from_configured? # => false
|
349
|
+
puts message.from # => nil
|
350
|
+
```
|
351
|
+
|
260
352
|
## Header Access
|
261
353
|
|
262
354
|
Access addressing information from message headers:
|
263
355
|
|
264
356
|
```ruby
|
265
357
|
class SampleMessage < SmartMessage::Base
|
266
|
-
|
267
|
-
|
268
|
-
|
358
|
+
# Using header block for configuration
|
359
|
+
header do
|
360
|
+
from 'sample-service'
|
361
|
+
to 'target-service'
|
362
|
+
reply_to 'callback-service'
|
363
|
+
end
|
269
364
|
|
270
365
|
property :content
|
271
366
|
end
|
272
367
|
|
273
368
|
message = SampleMessage.new(content: "Hello")
|
274
|
-
header = message._sm_header
|
275
369
|
|
370
|
+
# Access via shortcut methods (recommended)
|
371
|
+
puts message.from # => 'sample-service'
|
372
|
+
puts message.to # => 'target-service'
|
373
|
+
puts message.reply_to # => 'callback-service'
|
374
|
+
|
375
|
+
# Access via header object
|
376
|
+
header = message._sm_header
|
276
377
|
puts header.from # => 'sample-service'
|
277
378
|
puts header.to # => 'target-service'
|
278
379
|
puts header.reply_to # => 'callback-service'
|
279
380
|
puts header.uuid # => Generated UUID
|
280
381
|
puts header.message_class # => 'SampleMessage'
|
382
|
+
|
383
|
+
# Headers automatically sync with instance changes
|
384
|
+
message.to = 'new-target'
|
385
|
+
puts message.to # => 'new-target'
|
386
|
+
puts header.to # => 'new-target' (automatically updated)
|
281
387
|
```
|
282
388
|
|
283
389
|
## Integration with Dispatcher
|
data/docs/architecture.md
CHANGED
@@ -16,37 +16,53 @@ SmartMessage is designed around the principle that **messages should be independ
|
|
16
16
|
|
17
17
|
## Architecture Overview
|
18
18
|
|
19
|
-
```
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
19
|
+
```mermaid
|
20
|
+
graph TB
|
21
|
+
subgraph "SmartMessage Core"
|
22
|
+
Base[SmartMessage::Base]
|
23
|
+
Header[Message Header<br/>• UUID<br/>• Timestamps<br/>• Addressing]
|
24
|
+
Props[Message Properties<br/>• Business Data<br/>• Validation<br/>• Versioning]
|
25
|
+
end
|
26
|
+
|
27
|
+
subgraph "Plugin System"
|
28
|
+
Transport[Transport Plugin<br/>• publish()<br/>• subscribe()<br/>• Memory/Redis/STDOUT]
|
29
|
+
Serializer[Serializer Plugin<br/>• encode()<br/>• decode()<br/>• JSON/Custom]
|
30
|
+
Logger[Logger Plugin<br/>• Structured logging<br/>• Multiple outputs<br/>• Colorization]
|
31
|
+
end
|
32
|
+
|
33
|
+
subgraph "Message Processing"
|
34
|
+
Dispatcher[Dispatcher<br/>• Route messages<br/>• Thread pool<br/>• Subscriptions<br/>• DDQ management]
|
35
|
+
DDQ[Deduplication Queue<br/>• Handler-scoped<br/>• Memory/Redis storage<br/>• O(1) performance<br/>• Circular buffer]
|
36
|
+
Handlers[Message Handlers<br/>• Default handler<br/>• Block handlers<br/>• Proc handlers<br/>• Method handlers]
|
37
|
+
end
|
38
|
+
|
39
|
+
subgraph "Reliability Layer"
|
40
|
+
CircuitBreaker[Circuit Breaker<br/>• Failure thresholds<br/>• Automatic fallback<br/>• Recovery detection]
|
41
|
+
DLQ[Dead Letter Queue<br/>• Failed messages<br/>• Replay mechanism<br/>• JSON Lines format]
|
42
|
+
end
|
43
|
+
|
44
|
+
subgraph "Monitoring"
|
45
|
+
Stats[Statistics<br/>• Message counts<br/>• Processing metrics<br/>• Thread pool status]
|
46
|
+
Filters[Message Filtering<br/>• Entity-aware routing<br/>• Regex patterns<br/>• Broadcast handling]
|
47
|
+
end
|
48
|
+
|
49
|
+
Base --> Header
|
50
|
+
Base --> Props
|
51
|
+
Base --> Transport
|
52
|
+
Base --> Serializer
|
53
|
+
Base --> Logger
|
54
|
+
|
55
|
+
Transport --> Dispatcher
|
56
|
+
Dispatcher --> DDQ
|
57
|
+
Dispatcher --> Handlers
|
58
|
+
Dispatcher --> Stats
|
59
|
+
Dispatcher --> Filters
|
60
|
+
|
61
|
+
Transport --> CircuitBreaker
|
62
|
+
CircuitBreaker --> DLQ
|
63
|
+
|
64
|
+
DDQ -.-> Stats
|
65
|
+
Handlers -.-> Stats
|
50
66
|
```
|
51
67
|
|
52
68
|
## Core Components
|
@@ -126,38 +142,84 @@ end
|
|
126
142
|
|
127
143
|
### 4. Dispatcher
|
128
144
|
|
129
|
-
Routes incoming messages to appropriate handlers using concurrent processing.
|
145
|
+
Routes incoming messages to appropriate handlers using concurrent processing with integrated deduplication.
|
130
146
|
|
131
147
|
**Key Responsibilities:**
|
132
148
|
- Message routing based on class
|
133
|
-
- Thread pool management
|
149
|
+
- Thread pool management
|
134
150
|
- Subscription catalog management
|
135
|
-
-
|
151
|
+
- Handler-scoped DDQ management
|
152
|
+
- Message filtering and statistics collection
|
136
153
|
|
137
|
-
**Location:** `lib/smart_message/dispatcher.rb
|
154
|
+
**Location:** `lib/smart_message/dispatcher.rb`
|
138
155
|
|
139
156
|
```ruby
|
140
157
|
dispatcher = SmartMessage::Dispatcher.new
|
141
158
|
dispatcher.add("MyMessage", "MyMessage.process")
|
142
159
|
dispatcher.route(header, payload)
|
160
|
+
|
161
|
+
# DDQ integration is automatic when enabled
|
162
|
+
MyMessage.enable_deduplication!
|
163
|
+
```
|
164
|
+
|
165
|
+
### 5. Deduplication Queue (DDQ)
|
166
|
+
|
167
|
+
Handler-scoped message deduplication system preventing duplicate processing.
|
168
|
+
|
169
|
+
**Key Responsibilities:**
|
170
|
+
- UUID-based duplicate detection
|
171
|
+
- Handler isolation (each handler gets own DDQ)
|
172
|
+
- Memory and Redis storage backends
|
173
|
+
- O(1) performance with hybrid Array + Set data structure
|
174
|
+
|
175
|
+
**Architecture:**
|
176
|
+
```mermaid
|
177
|
+
graph LR
|
178
|
+
subgraph "Handler A DDQ"
|
179
|
+
A1[Circular Array]
|
180
|
+
A2[Lookup Set]
|
181
|
+
A3[Mutex Lock]
|
182
|
+
end
|
183
|
+
|
184
|
+
subgraph "Handler B DDQ"
|
185
|
+
B1[Circular Array]
|
186
|
+
B2[Lookup Set]
|
187
|
+
B3[Mutex Lock]
|
188
|
+
end
|
189
|
+
|
190
|
+
Message[Incoming Message<br/>UUID: abc-123] --> Dispatcher
|
191
|
+
Dispatcher --> |Check Handler A| A2
|
192
|
+
Dispatcher --> |Check Handler B| B2
|
193
|
+
|
194
|
+
A2 --> |Not Found| ProcessA[Process with Handler A]
|
195
|
+
B2 --> |Found| SkipB[Skip Handler B - Duplicate]
|
196
|
+
|
197
|
+
ProcessA --> |Add UUID| A1
|
198
|
+
ProcessA --> |Add UUID| A2
|
143
199
|
```
|
144
200
|
|
145
|
-
|
201
|
+
**Location:** `lib/smart_message/deduplication.rb`, `lib/smart_message/ddq/`
|
202
|
+
|
203
|
+
### 6. Message Headers
|
146
204
|
|
147
|
-
Standard metadata attached to every message.
|
205
|
+
Standard metadata attached to every message with entity addressing support.
|
148
206
|
|
149
207
|
**Key Responsibilities:**
|
150
208
|
- Message identification (UUID)
|
151
|
-
- Routing information (message class)
|
209
|
+
- Routing information (message class, version)
|
152
210
|
- Tracking data (timestamps, process IDs)
|
211
|
+
- Entity addressing (from, to, reply_to)
|
153
212
|
|
154
|
-
**Location:** `lib/smart_message/header.rb
|
213
|
+
**Location:** `lib/smart_message/header.rb`
|
155
214
|
|
156
215
|
```ruby
|
157
216
|
header = message._sm_header
|
158
217
|
puts header.uuid # "550e8400-e29b-41d4-a716-446655440000"
|
159
218
|
puts header.message_class # "MyMessage"
|
160
219
|
puts header.published_at # 2025-08-17 10:30:00 UTC
|
220
|
+
puts header.from # "payment-service"
|
221
|
+
puts header.to # "order-service"
|
222
|
+
puts header.version # 1
|
161
223
|
```
|
162
224
|
|
163
225
|
## Message Lifecycle
|
@@ -177,17 +239,27 @@ end
|
|
177
239
|
|
178
240
|
### 2. Subscription Phase
|
179
241
|
```ruby
|
242
|
+
# Basic subscription
|
180
243
|
OrderMessage.subscribe
|
181
|
-
|
244
|
+
|
245
|
+
# Subscription with filtering
|
246
|
+
OrderMessage.subscribe(from: /^payment-.*/, to: 'order-service')
|
247
|
+
OrderMessage.subscribe('PaymentService.process', broadcast: true)
|
248
|
+
|
249
|
+
# Each subscription gets its own DDQ automatically
|
250
|
+
# DDQ Key: "OrderMessage:OrderMessage.process"
|
251
|
+
# DDQ Key: "OrderMessage:PaymentService.process"
|
182
252
|
```
|
183
253
|
|
184
254
|
### 3. Publishing Phase
|
185
255
|
```ruby
|
186
256
|
order = OrderMessage.new(order_id: "123", amount: 99.99)
|
257
|
+
order.from("order-service").to("payment-service")
|
187
258
|
order.publish
|
188
|
-
# 1. Creates header with UUID, timestamp,
|
189
|
-
# 2. Encodes message via serializer
|
259
|
+
# 1. Creates header with UUID, timestamp, addressing
|
260
|
+
# 2. Encodes message via serializer
|
190
261
|
# 3. Sends via transport
|
262
|
+
# 4. Circuit breaker monitors for failures
|
191
263
|
```
|
192
264
|
|
193
265
|
### 4. Receiving Phase
|
@@ -195,9 +267,10 @@ order.publish
|
|
195
267
|
# Transport receives message
|
196
268
|
transport.receive(header, payload)
|
197
269
|
# 1. Routes to dispatcher
|
198
|
-
# 2. Dispatcher
|
199
|
-
# 3.
|
200
|
-
# 4.
|
270
|
+
# 2. Dispatcher checks DDQ for duplicates per handler
|
271
|
+
# 3. Applies message filters (from/to/broadcast)
|
272
|
+
# 4. Spawns thread for processing matching handlers
|
273
|
+
# 5. Marks UUID as processed in handler's DDQ
|
201
274
|
```
|
202
275
|
|
203
276
|
### 5. Message Handler Processing
|
@@ -345,11 +418,69 @@ module SmartMessage::Errors
|
|
345
418
|
end
|
346
419
|
```
|
347
420
|
|
421
|
+
## Reliability & Fault Tolerance
|
422
|
+
|
423
|
+
### Circuit Breaker Integration
|
424
|
+
|
425
|
+
SmartMessage integrates BreakerMachines for production-grade reliability:
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
# Transport operations protected by circuit breakers
|
429
|
+
class MyTransport < SmartMessage::Transport::Base
|
430
|
+
circuit :transport_publish do
|
431
|
+
threshold failures: 5, within: 30.seconds
|
432
|
+
reset_after 15.seconds
|
433
|
+
fallback SmartMessage::CircuitBreaker::Fallbacks.dead_letter_queue
|
434
|
+
end
|
435
|
+
end
|
436
|
+
```
|
437
|
+
|
438
|
+
**Circuit Breaker States:**
|
439
|
+
- **Closed**: Normal operation, requests pass through
|
440
|
+
- **Open**: Threshold exceeded, requests fail fast
|
441
|
+
- **Half-Open**: Testing if service recovered
|
442
|
+
|
443
|
+
### Dead Letter Queue
|
444
|
+
|
445
|
+
Failed messages are automatically captured in the Dead Letter Queue:
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
# Automatic capture when circuit breaker trips
|
449
|
+
message.publish # If transport fails, goes to DLQ
|
450
|
+
|
451
|
+
# Manual capture for business logic failures
|
452
|
+
dlq = SmartMessage::DeadLetterQueue.default
|
453
|
+
dlq.enqueue(header, payload, error: "Validation failed")
|
454
|
+
```
|
455
|
+
|
456
|
+
**DLQ Architecture:**
|
457
|
+
|
458
|
+
```mermaid
|
459
|
+
graph TB
|
460
|
+
Publish[Message Publishing]
|
461
|
+
CB[Circuit Breaker<br/>Monitoring]
|
462
|
+
Transport[Transport<br/>Success]
|
463
|
+
DLQ[Dead Letter Queue<br/>Failure Storage]
|
464
|
+
Replay[Replay Mechanism<br/>Manual/Automated]
|
465
|
+
|
466
|
+
Publish --> CB
|
467
|
+
CB --> |Success| Transport
|
468
|
+
CB --> |Failure| DLQ
|
469
|
+
DLQ --> Replay
|
470
|
+
Replay --> |Retry| Publish
|
471
|
+
```
|
472
|
+
|
473
|
+
**DLQ Features:**
|
474
|
+
- JSON Lines format for efficient append operations
|
475
|
+
- FIFO queue operations with thread safety
|
476
|
+
- Replay capabilities with transport override
|
477
|
+
- Administrative tools for filtering and analysis
|
478
|
+
|
348
479
|
## Statistics & Monitoring
|
349
480
|
|
350
481
|
### Built-in Statistics
|
351
482
|
|
352
|
-
SmartMessage automatically collects processing statistics:
|
483
|
+
SmartMessage automatically collects processing statistics including DDQ metrics:
|
353
484
|
|
354
485
|
```ruby
|
355
486
|
# Statistics are collected for:
|
@@ -359,6 +490,11 @@ SS.add(message_class, process_method, 'routed')
|
|
359
490
|
# Access statistics
|
360
491
|
puts SS.stat
|
361
492
|
puts SS.get("MyMessage", "publish")
|
493
|
+
|
494
|
+
# DDQ-specific statistics
|
495
|
+
stats = OrderMessage.ddq_stats
|
496
|
+
puts "DDQ utilization: #{stats[:utilization]}%"
|
497
|
+
puts "Current count: #{stats[:current_count]}"
|
362
498
|
```
|
363
499
|
|
364
500
|
### Monitoring Points
|
@@ -367,6 +503,8 @@ puts SS.get("MyMessage", "publish")
|
|
367
503
|
2. **Message Routing**: Count of routed messages per processor
|
368
504
|
3. **Thread Pool**: Queue length, completed tasks, running status
|
369
505
|
4. **Transport Status**: Connection status, message counts
|
506
|
+
5. **DDQ Metrics**: Utilization, duplicate detection rates, memory usage
|
507
|
+
6. **Message Filtering**: Filter match rates, entity-aware routing statistics
|
370
508
|
|
371
509
|
## Configuration Architecture
|
372
510
|
|