smart_message 0.0.9 → 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.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +38 -0
  3. data/.gitignore +5 -0
  4. data/CHANGELOG.md +53 -0
  5. data/Gemfile.lock +35 -4
  6. data/README.md +265 -69
  7. data/Rakefile +29 -4
  8. data/docs/assets/images/ddq_architecture.svg +130 -0
  9. data/docs/assets/images/dlq_architecture.svg +115 -0
  10. data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
  11. data/docs/assets/images/enhanced-fluent-api.svg +149 -0
  12. data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
  13. data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
  14. data/docs/assets/images/fluent-api-demo.svg +59 -0
  15. data/docs/assets/images/performance-comparison.svg +161 -0
  16. data/docs/assets/images/redis-basic-architecture.svg +53 -0
  17. data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
  18. data/docs/assets/images/redis-queue-architecture.svg +101 -0
  19. data/docs/assets/images/smart_message.jpg +0 -0
  20. data/docs/assets/images/smart_message_walking.jpg +0 -0
  21. data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
  22. data/docs/assets/images/transport-comparison-matrix.svg +171 -0
  23. data/docs/assets/javascripts/mathjax.js +17 -0
  24. data/docs/assets/stylesheets/extra.css +51 -0
  25. data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
  26. data/docs/{architecture.md → core-concepts/architecture.md} +129 -119
  27. data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
  28. data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
  29. data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
  30. data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
  31. data/docs/{examples.md → getting-started/examples.md} +115 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
  33. data/docs/guides/redis-queue-getting-started.md +697 -0
  34. data/docs/guides/redis-queue-patterns.md +889 -0
  35. data/docs/guides/redis-queue-production.md +1091 -0
  36. data/docs/index.md +64 -0
  37. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  38. data/docs/{logging.md → reference/logging.md} +1 -1
  39. data/docs/reference/message-deduplication.md +489 -0
  40. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  41. data/docs/{serializers.md → reference/serializers.md} +3 -5
  42. data/docs/{transports.md → reference/transports.md} +133 -11
  43. data/docs/transports/memory-transport.md +374 -0
  44. data/docs/transports/redis-enhanced-transport.md +524 -0
  45. data/docs/transports/redis-queue-transport.md +1304 -0
  46. data/docs/transports/redis-transport-comparison.md +496 -0
  47. data/docs/transports/redis-transport.md +509 -0
  48. data/examples/README.md +98 -5
  49. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  50. data/examples/city_scenario/README.md +515 -0
  51. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  52. data/examples/city_scenario/citizen.rb +195 -0
  53. data/examples/city_scenario/city_diagram.svg +125 -0
  54. data/examples/city_scenario/common/health_monitor.rb +80 -0
  55. data/examples/city_scenario/common/logger.rb +30 -0
  56. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  57. data/examples/city_scenario/fire_department.rb +446 -0
  58. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  59. data/examples/city_scenario/health_department.rb +100 -0
  60. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  61. data/examples/city_scenario/house.rb +244 -0
  62. data/examples/city_scenario/local_bank.rb +217 -0
  63. data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
  64. data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
  65. data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
  66. data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
  67. data/examples/city_scenario/messages/health_check_message.rb +22 -0
  68. data/examples/city_scenario/messages/health_status_message.rb +35 -0
  69. data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
  70. data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
  71. data/examples/city_scenario/police_department.rb +316 -0
  72. data/examples/city_scenario/redis_monitor.rb +129 -0
  73. data/examples/city_scenario/redis_stats.rb +743 -0
  74. data/examples/city_scenario/room_for_improvement.md +240 -0
  75. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  76. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  77. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  78. data/examples/city_scenario/start_demo.sh +236 -0
  79. data/examples/city_scenario/stop_demo.sh +106 -0
  80. data/examples/city_scenario/visitor.rb +631 -0
  81. data/examples/memory/01_message_deduplication_demo.rb +209 -0
  82. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
  83. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
  84. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
  85. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
  86. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  87. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
  88. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
  89. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
  90. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
  91. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
  92. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
  93. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
  94. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
  95. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
  96. data/examples/memory/README.md +163 -0
  97. data/examples/memory/memory_transport_architecture.svg +90 -0
  98. data/examples/memory/point_to_point_pattern.svg +94 -0
  99. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  100. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
  101. data/examples/redis/README.md +230 -0
  102. data/examples/redis/alert_system_flow.svg +127 -0
  103. data/examples/redis/dashboard_status_flow.svg +107 -0
  104. data/examples/redis/device_command_flow.svg +113 -0
  105. data/examples/redis/redis_transport_architecture.svg +115 -0
  106. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  107. data/examples/redis/smart_home_system_architecture.svg +133 -0
  108. data/examples/redis_enhanced/README.md +319 -0
  109. data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
  110. data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
  111. data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
  112. data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
  113. data/examples/redis_queue/01_basic_messaging.rb +221 -0
  114. data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
  115. data/examples/redis_queue/02_pattern_routing.rb +405 -0
  116. data/examples/redis_queue/03_fluent_api.rb +422 -0
  117. data/examples/redis_queue/04_load_balancing.rb +486 -0
  118. data/examples/redis_queue/05_microservices.rb +735 -0
  119. data/examples/redis_queue/06_emergency_alerts.rb +777 -0
  120. data/examples/redis_queue/07_queue_management.rb +587 -0
  121. data/examples/redis_queue/README.md +366 -0
  122. data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
  123. data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
  124. data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
  125. data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
  126. data/examples/redis_queue/redis_queue_architecture.svg +148 -0
  127. data/ideas/README.md +41 -0
  128. data/ideas/agents.md +1001 -0
  129. data/ideas/database_transport.md +980 -0
  130. data/ideas/improvement.md +359 -0
  131. data/ideas/meshage.md +1788 -0
  132. data/ideas/message_discovery.md +178 -0
  133. data/ideas/message_schema.md +1381 -0
  134. data/lib/smart_message/.idea/.gitignore +8 -0
  135. data/lib/smart_message/.idea/markdown.xml +6 -0
  136. data/lib/smart_message/.idea/misc.xml +4 -0
  137. data/lib/smart_message/.idea/modules.xml +8 -0
  138. data/lib/smart_message/.idea/smart_message.iml +16 -0
  139. data/lib/smart_message/.idea/vcs.xml +6 -0
  140. data/lib/smart_message/addressing.rb +15 -0
  141. data/lib/smart_message/base.rb +2 -2
  142. data/lib/smart_message/configuration.rb +1 -1
  143. data/lib/smart_message/ddq/base.rb +71 -0
  144. data/lib/smart_message/ddq/memory.rb +109 -0
  145. data/lib/smart_message/ddq/redis.rb +168 -0
  146. data/lib/smart_message/ddq.rb +31 -0
  147. data/lib/smart_message/deduplication.rb +174 -0
  148. data/lib/smart_message/dispatcher.rb +175 -18
  149. data/lib/smart_message/logger.rb +15 -4
  150. data/lib/smart_message/plugins.rb +5 -2
  151. data/lib/smart_message/serializer.rb +14 -0
  152. data/lib/smart_message/subscription.rb +10 -7
  153. data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
  154. data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
  155. data/lib/smart_message/transport/registry.rb +1 -0
  156. data/lib/smart_message/transport.rb +34 -1
  157. data/lib/smart_message/version.rb +1 -1
  158. data/lib/smart_message.rb +5 -52
  159. data/mkdocs.yml +184 -0
  160. data/p2p_plan.md +326 -0
  161. data/p2p_roadmap.md +287 -0
  162. data/smart_message.gemspec +2 -0
  163. data/smart_message.svg +51 -0
  164. metadata +175 -42
  165. data/docs/README.md +0 -57
  166. data/examples/dead_letters.jsonl +0 -12
  167. data/examples/temp.txt +0 -94
  168. data/examples/tmux_chat/README.md +0 -283
  169. data/examples/tmux_chat/bot_agent.rb +0 -278
  170. data/examples/tmux_chat/human_agent.rb +0 -199
  171. data/examples/tmux_chat/room_monitor.rb +0 -160
  172. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  173. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  174. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  175. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  176. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -16,38 +16,7 @@ SmartMessage is designed around the principle that **messages should be independ
16
16
 
17
17
  ## Architecture Overview
18
18
 
19
- ```
20
- ┌─────────────────────────────────────────────────────────────┐
21
- │ SmartMessage::Base │
22
- │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│
23
- │ │ Message │ │ Transport │ │ Serializer ││
24
- │ │ Properties │ │ Plugin │ │ Plugin ││
25
- │ │ │ │ │ │ ││
26
- │ │ • user_id │ │ • publish() │ │ • encode() ││
27
- │ │ • action │ │ • subscribe() │ │ • decode() ││
28
- │ │ • timestamp │ │ • receive() │ │ ││
29
- │ └─────────────────┘ └─────────────────┘ └─────────────────┘│
30
- └─────────────────────────────────────────────────────────────┘
31
-
32
-
33
- ┌─────────────────────┐
34
- │ Dispatcher │
35
- │ │
36
- │ • Route messages │
37
- │ • Thread pool │
38
- │ • Subscriptions │
39
- └─────────────────────┘
40
-
41
-
42
- ┌─────────────────────┐
43
- │ Message Handlers │
44
- │ │
45
- │ • Default handler │
46
- │ • Block handlers │
47
- │ • Proc handlers │
48
- │ • Method handlers │
49
- └─────────────────────┘
50
- ```
19
+ ![SmartMessage Architecture Overview](../assets/images/smartmessage_architecture_overview.svg)
51
20
 
52
21
  ## Core Components
53
22
 
@@ -61,7 +30,7 @@ The foundation class that all messages inherit from, built on `Hashie::Dash`.
61
30
  - Message lifecycle management
62
31
  - Header generation and management
63
32
 
64
- **Location:** `lib/smart_message/base.rb:11-278`
33
+ **Location:** `lib/smart_message/base.rb:17-199`
65
34
 
66
35
  ```ruby
67
36
  class MyMessage < SmartMessage::Base
@@ -91,12 +60,13 @@ Handles message delivery and routing between systems.
91
60
  ```ruby
92
61
  # Transport interface
93
62
  class CustomTransport < SmartMessage::Transport::Base
94
- def publish(message_header, message_payload)
63
+ def do_publish(message_class, serialized_message)
95
64
  # Send message via your transport
96
65
  end
97
66
 
98
- def subscribe(message_class, process_method)
99
- # Set up subscription
67
+ def subscribe(message_class, process_method, filter_options = {})
68
+ # Set up subscription via dispatcher
69
+ @dispatcher.add(message_class, process_method, filter_options)
100
70
  end
101
71
  end
102
72
  ```
@@ -114,50 +84,86 @@ Handles encoding and decoding of message content.
114
84
 
115
85
  ```ruby
116
86
  class CustomSerializer < SmartMessage::Serializer::Base
117
- def encode(message_instance)
118
- # Convert to wire format
87
+ def do_encode(message_instance)
88
+ # Convert message instance to wire format
89
+ # Default implementation uses message_instance.to_h
119
90
  end
120
91
 
121
- def decode(payload)
122
- # Convert from wire format
92
+ def do_decode(payload)
93
+ # Convert from wire format back to hash
94
+ # Must return hash compatible with message initialization
123
95
  end
124
96
  end
125
97
  ```
126
98
 
127
99
  ### 4. Dispatcher
128
100
 
129
- Routes incoming messages to appropriate handlers using concurrent processing.
101
+ Routes incoming messages to appropriate handlers using concurrent processing with integrated deduplication.
130
102
 
131
103
  **Key Responsibilities:**
132
104
  - Message routing based on class
133
- - Thread pool management
105
+ - Thread pool management
134
106
  - Subscription catalog management
135
- - Statistics collection
107
+ - Handler-scoped DDQ management
108
+ - Message filtering and statistics collection
136
109
 
137
- **Location:** `lib/smart_message/dispatcher.rb:11-147`
110
+ **Location:** `lib/smart_message/dispatcher.rb`
138
111
 
139
112
  ```ruby
140
113
  dispatcher = SmartMessage::Dispatcher.new
141
114
  dispatcher.add("MyMessage", "MyMessage.process")
142
- dispatcher.route(header, payload)
115
+ dispatcher.route(decoded_message)
116
+
117
+ # DDQ integration is automatic when enabled
118
+ MyMessage.enable_deduplication!
143
119
  ```
144
120
 
145
- ### 5. Message Headers
121
+ ### 5. Deduplication Queue (DDQ)
122
+
123
+ Handler-scoped message deduplication system preventing duplicate processing.
124
+
125
+ **Key Responsibilities:**
126
+ - UUID-based duplicate detection
127
+ - Handler isolation (each handler gets own DDQ)
128
+ - Memory and Redis storage backends
129
+ - O(1) performance with hybrid Array + Set data structure
130
+
131
+ **Architecture:**
132
+
133
+ ![DDQ Architecture](../assets/images/ddq_architecture.svg)
134
+
135
+ **Location:** `lib/smart_message/deduplication.rb`, `lib/smart_message/ddq/`
136
+
137
+ **Key Features:**
138
+ - Handler-scoped deduplication (each handler gets its own DDQ)
139
+ - UUID-based duplicate detection
140
+ - Multiple storage backends (Memory, Redis)
141
+ - O(1) performance with hybrid Array + Set data structure
142
+ - Thread-safe operations with mutex locks
146
143
 
147
- Standard metadata attached to every message.
144
+ ### 6. Message Headers
145
+
146
+ Standard metadata attached to every message with entity addressing support.
148
147
 
149
148
  **Key Responsibilities:**
150
149
  - Message identification (UUID)
151
- - Routing information (message class)
150
+ - Routing information (message class, version)
152
151
  - Tracking data (timestamps, process IDs)
152
+ - Entity addressing (from, to, reply_to)
153
153
 
154
- **Location:** `lib/smart_message/header.rb:9-20`
154
+ **Location:** `lib/smart_message/header.rb`
155
155
 
156
156
  ```ruby
157
157
  header = message._sm_header
158
158
  puts header.uuid # "550e8400-e29b-41d4-a716-446655440000"
159
159
  puts header.message_class # "MyMessage"
160
160
  puts header.published_at # 2025-08-17 10:30:00 UTC
161
+ puts header.publisher_pid # 12345
162
+ puts header.from # "payment-service"
163
+ puts header.to # "order-service"
164
+ puts header.reply_to # "payment-service" (defaults to from)
165
+ puts header.version # 1
166
+ puts header.serializer # "SmartMessage::Serializer::JSON"
161
167
  ```
162
168
 
163
169
  ## Message Lifecycle
@@ -170,34 +176,46 @@ class OrderMessage < SmartMessage::Base
170
176
 
171
177
  config do
172
178
  transport SmartMessage::Transport.create(:memory)
173
- serializer SmartMessage::Serializer::JSON.new
179
+ serializer SmartMessage::Serializer::Json.new
174
180
  end
175
181
  end
176
182
  ```
177
183
 
178
184
  ### 2. Subscription Phase
179
185
  ```ruby
186
+ # Basic subscription
180
187
  OrderMessage.subscribe
181
- # Registers "OrderMessage.process" with dispatcher
188
+
189
+ # Subscription with filtering
190
+ OrderMessage.subscribe(from: /^payment-.*/, to: 'order-service')
191
+ OrderMessage.subscribe('PaymentService.process', broadcast: true)
192
+
193
+ # Each subscription gets its own DDQ automatically
194
+ # DDQ Key: "OrderMessage:OrderMessage.process"
195
+ # DDQ Key: "OrderMessage:PaymentService.process"
182
196
  ```
183
197
 
184
198
  ### 3. Publishing Phase
185
199
  ```ruby
186
200
  order = OrderMessage.new(order_id: "123", amount: 99.99)
201
+ order.from("order-service").to("payment-service")
187
202
  order.publish
188
- # 1. Creates header with UUID, timestamp, etc.
189
- # 2. Encodes message via serializer
203
+ # 1. Creates header with UUID, timestamp, addressing
204
+ # 2. Encodes message via serializer
190
205
  # 3. Sends via transport
206
+ # 4. Circuit breaker monitors for failures
191
207
  ```
192
208
 
193
209
  ### 4. Receiving Phase
194
210
  ```ruby
195
- # Transport receives message
196
- transport.receive(header, payload)
197
- # 1. Routes to dispatcher
198
- # 2. Dispatcher finds subscribers
199
- # 3. Spawns thread for processing
200
- # 4. Calls registered message handlers
211
+ # Transport receives serialized message
212
+ transport.receive(message_class, serialized_message)
213
+ # 1. Decodes message using class's configured serializer
214
+ # 2. Routes decoded message to dispatcher
215
+ # 3. Dispatcher checks DDQ for duplicates per handler
216
+ # 4. Applies message filters (from/to/broadcast)
217
+ # 5. Spawns thread for processing matching handlers
218
+ # 6. Marks UUID as processed in handler's DDQ after successful processing
201
219
  ```
202
220
 
203
221
  ### 5. Message Handler Processing
@@ -205,43 +223,42 @@ transport.receive(header, payload)
205
223
  SmartMessage supports multiple handler types, routed through the dispatcher:
206
224
 
207
225
  ```ruby
208
- # Default handler (self.process method)
209
- def self.process(message_header, message_payload)
210
- data = JSON.parse(message_payload)
211
- order = new(data)
226
+ # Default handler (self.process method) - receives decoded message instance
227
+ def self.process(decoded_message)
228
+ order = decoded_message # Already a fully decoded OrderMessage instance
212
229
  fulfill_order(order)
213
230
  end
214
231
 
215
- # Block handler (inline processing)
216
- OrderMessage.subscribe do |header, payload|
217
- data = JSON.parse(payload)
218
- quick_processing(data)
232
+ # Block handler (inline processing) - receives decoded message instance
233
+ OrderMessage.subscribe do |decoded_message|
234
+ quick_processing(decoded_message)
219
235
  end
220
236
 
221
- # Proc handler (reusable across message types)
222
- audit_proc = proc do |header, payload|
223
- AuditService.log_message(header.message_class, payload)
237
+ # Proc handler (reusable across message types) - receives decoded message instance
238
+ audit_proc = proc do |decoded_message|
239
+ AuditService.log_message(decoded_message.class.name, decoded_message)
224
240
  end
225
241
  OrderMessage.subscribe(audit_proc)
226
242
 
227
- # Method handler (service class processing)
243
+ # Method handler (service class processing) - receives decoded message instance
228
244
  class OrderService
229
- def self.process_order(header, payload)
230
- data = JSON.parse(payload)
231
- complex_business_logic(data)
245
+ def self.process_order(decoded_message)
246
+ complex_business_logic(decoded_message)
232
247
  end
233
248
  end
234
249
  OrderMessage.subscribe("OrderService.process_order")
235
250
  ```
236
251
 
237
252
  **Handler Routing Process:**
238
- 1. Dispatcher receives message and header
253
+ 1. Dispatcher receives decoded message instance
239
254
  2. Looks up all registered handlers for message class
240
- 3. For each handler:
255
+ 3. For each handler that matches filters:
256
+ - Checks DDQ for duplicates (handler-scoped)
241
257
  - **String handlers**: Resolves to class method via constantize
242
258
  - **Proc handlers**: Calls proc directly from registry
243
- 4. Executes handlers in parallel threads
244
- 5. Collects statistics and handles errors
259
+ 4. Executes handlers in parallel threads with circuit breaker protection
260
+ 5. Marks UUID as processed in handler's DDQ after successful completion
261
+ 6. Collects statistics and handles errors
245
262
 
246
263
  ## Plugin System Architecture
247
264
 
@@ -291,10 +308,15 @@ end
291
308
  The dispatcher uses `Concurrent::CachedThreadPool` for processing:
292
309
 
293
310
  ```ruby
294
- # Each message processing happens in its own thread
311
+ # Each message processing happens in its own thread with circuit breaker protection
295
312
  @router_pool.post do
296
- # Message processing happens here
297
- target_class.constantize.process(header, payload)
313
+ circuit_result = circuit(:message_processor).wrap do
314
+ if proc_handler?(message_processor)
315
+ SmartMessage::Base.call_proc_handler(message_processor, decoded_message)
316
+ else
317
+ target_class.constantize.method(class_method).call(decoded_message)
318
+ end
319
+ end
298
320
  end
299
321
  ```
300
322
 
@@ -323,11 +345,13 @@ puts "Completed tasks: #{status[:completed_task_count]}"
323
345
  Processing exceptions are isolated to prevent cascade failures:
324
346
 
325
347
  ```ruby
326
- begin
327
- target_class.constantize.process(header, payload)
328
- rescue Exception => e
329
- # Log error but don't crash the dispatcher
330
- # TODO: Add proper exception logging
348
+ circuit_result = circuit(:message_processor).wrap do
349
+ # Handler execution with circuit breaker protection
350
+ end
351
+
352
+ # Handle circuit breaker fallback responses
353
+ if circuit_result.is_a?(Hash) && circuit_result[:circuit_breaker]
354
+ handle_circuit_breaker_fallback(circuit_result, decoded_message, message_processor)
331
355
  end
332
356
  ```
333
357
 
@@ -342,6 +366,7 @@ module SmartMessage::Errors
342
366
  class NotImplemented < RuntimeError; end
343
367
  class ReceivedMessageNotSubscribed < RuntimeError; end
344
368
  class UnknownMessageClass < RuntimeError; end
369
+ class ValidationError < RuntimeError; end
345
370
  end
346
371
  ```
347
372
 
@@ -352,13 +377,12 @@ end
352
377
  SmartMessage integrates BreakerMachines for production-grade reliability:
353
378
 
354
379
  ```ruby
355
- # Transport operations protected by circuit breakers
380
+ # Circuit breakers are automatically configured for all transports
356
381
  class MyTransport < SmartMessage::Transport::Base
357
- circuit :transport_publish do
358
- threshold failures: 5, within: 30.seconds
359
- reset_after 15.seconds
360
- fallback SmartMessage::CircuitBreaker::Fallbacks.dead_letter_queue
361
- end
382
+ # Inherits circuit breaker configuration:
383
+ # - :transport_publish for publishing operations
384
+ # - :transport_subscribe for subscription operations
385
+ # - Automatic DLQ fallback for failed publishes
362
386
  end
363
387
  ```
364
388
 
@@ -377,35 +401,12 @@ message.publish # If transport fails, goes to DLQ
377
401
 
378
402
  # Manual capture for business logic failures
379
403
  dlq = SmartMessage::DeadLetterQueue.default
380
- dlq.enqueue(header, payload, error: "Validation failed")
404
+ dlq.enqueue(decoded_message, error: "Validation failed", transport: "manual")
381
405
  ```
382
406
 
383
407
  **DLQ Architecture:**
384
408
 
385
- ```
386
- ┌─────────────────────────────────────────────────────────────┐
387
- │ Message Publishing │
388
- │ │ │
389
- │ ▼ │
390
- │ ┌─────────────────┐ │
391
- │ │ Circuit Breaker │ │
392
- │ │ (Monitoring) │ │
393
- │ └─────────────────┘ │
394
- │ │ │ │
395
- │ Success │ │ Failure │
396
- │ ▼ ▼ │
397
- │ ┌──────────┐ ┌─────────────┐ │
398
- │ │Transport │ │Dead Letter │ │
399
- │ │ │ │ Queue │ │
400
- │ └──────────┘ └─────────────┘ │
401
- │ │ │
402
- │ ▼ │
403
- │ ┌─────────────┐ │
404
- │ │ Replay │ │
405
- │ │ Mechanism │ │
406
- │ └─────────────┘ │
407
- └─────────────────────────────────────────────────────────────┘
408
- ```
409
+ ![Dead Letter Queue Architecture](../assets/images/dlq_architecture.svg)
409
410
 
410
411
  **DLQ Features:**
411
412
  - JSON Lines format for efficient append operations
@@ -417,7 +418,7 @@ dlq.enqueue(header, payload, error: "Validation failed")
417
418
 
418
419
  ### Built-in Statistics
419
420
 
420
- SmartMessage automatically collects processing statistics:
421
+ SmartMessage automatically collects processing statistics including DDQ metrics:
421
422
 
422
423
  ```ruby
423
424
  # Statistics are collected for:
@@ -427,6 +428,11 @@ SS.add(message_class, process_method, 'routed')
427
428
  # Access statistics
428
429
  puts SS.stat
429
430
  puts SS.get("MyMessage", "publish")
431
+
432
+ # DDQ-specific statistics
433
+ stats = OrderMessage.ddq_stats
434
+ puts "DDQ utilization: #{stats[:utilization]}%"
435
+ puts "Current count: #{stats[:current_count]}"
430
436
  ```
431
437
 
432
438
  ### Monitoring Points
@@ -435,6 +441,8 @@ puts SS.get("MyMessage", "publish")
435
441
  2. **Message Routing**: Count of routed messages per processor
436
442
  3. **Thread Pool**: Queue length, completed tasks, running status
437
443
  4. **Transport Status**: Connection status, message counts
444
+ 5. **DDQ Metrics**: Utilization, duplicate detection rates, memory usage
445
+ 6. **Message Filtering**: Filter match rates, entity-aware routing statistics
438
446
 
439
447
  ## Configuration Architecture
440
448
 
@@ -466,7 +474,9 @@ When a message needs a plugin:
466
474
 
467
475
  ```ruby
468
476
  def transport
469
- @transport || @@transport || raise(Errors::TransportNotConfigured)
477
+ @transport || self.class.class_variable_get(:@@transport) || raise(Errors::TransportNotConfigured)
478
+ rescue NameError
479
+ raise(Errors::TransportNotConfigured)
470
480
  end
471
481
  ```
472
482
 
@@ -187,10 +187,10 @@ puts dispatcher.subscribers["MyMessage"]
187
187
  When a transport receives a message, it calls the dispatcher:
188
188
 
189
189
  ```ruby
190
- # Transport receives message and routes it
191
- transport.receive(message_header, message_payload)
192
- # This internally calls:
193
- dispatcher.route(message_header, message_payload)
190
+ # Transport receives serialized message and routes it
191
+ transport.receive(message_class, serialized_message)
192
+ # This internally decodes the message and calls:
193
+ dispatcher.route(decoded_message)
194
194
  ```
195
195
 
196
196
  ### 2. Subscription Lookup
@@ -198,12 +198,12 @@ dispatcher.route(message_header, message_payload)
198
198
  The dispatcher finds all registered handlers:
199
199
 
200
200
  ```ruby
201
- def route(message_header, message_payload)
202
- message_klass = message_header.message_class
201
+ def route(decoded_message)
202
+ message_klass = decoded_message._sm_header.message_class
203
203
  return nil if @subscribers[message_klass].empty?
204
204
 
205
- @subscribers[message_klass].each do |message_processor|
206
- # Process each handler
205
+ @subscribers[message_klass].each do |subscription|
206
+ # Process each handler with filters
207
207
  end
208
208
  end
209
209
  ```
@@ -213,16 +213,17 @@ end
213
213
  Each handler is processed in its own thread, with support for both method and proc handlers:
214
214
 
215
215
  ```ruby
216
- @subscribers[message_klass].each do |message_processor|
216
+ @subscribers[message_klass].each do |subscription|
217
+ message_processor = subscription[:process_method]
217
218
  SS.add(message_klass, message_processor, 'routed')
218
219
 
219
220
  @router_pool.post do
220
- # This runs in a separate thread
221
- begin
221
+ # This runs in a separate thread with circuit breaker protection
222
+ circuit_result = circuit(:message_processor).wrap do
222
223
  # Check if this is a proc handler or a regular method call
223
224
  if proc_handler?(message_processor)
224
225
  # Call the proc handler via SmartMessage::Base
225
- SmartMessage::Base.call_proc_handler(message_processor, message_header, message_payload)
226
+ SmartMessage::Base.call_proc_handler(message_processor, decoded_message)
226
227
  else
227
228
  # Original method call logic
228
229
  parts = message_processor.split('.')
@@ -231,11 +232,13 @@ Each handler is processed in its own thread, with support for both method and pr
231
232
 
232
233
  target_klass.constantize
233
234
  .method(class_method)
234
- .call(message_header, message_payload)
235
+ .call(decoded_message)
235
236
  end
236
- rescue Exception => e
237
- # Error handling - doesn't crash the dispatcher
238
- puts "Error processing message: #{e.message}" if $DEBUG
237
+ end
238
+
239
+ # Handle circuit breaker fallback if triggered
240
+ if circuit_result.is_a?(Hash) && circuit_result[:circuit_breaker]
241
+ handle_circuit_breaker_fallback(circuit_result, decoded_message, message_processor)
239
242
  end
240
243
  end
241
244
  end
@@ -691,7 +694,7 @@ RSpec.describe "Message Processing" do
691
694
  before do
692
695
  TestMessage.config do
693
696
  transport transport
694
- serializer SmartMessage::Serializer::JSON.new
697
+ serializer SmartMessage::Serializer::Json.new
695
698
  end
696
699
 
697
700
  TestMessage.subscribe
@@ -710,7 +713,4 @@ end
710
713
 
711
714
  ## Next Steps
712
715
 
713
- - [Thread Safety](thread-safety.md) - Understanding concurrent processing
714
- - [Statistics & Monitoring](monitoring.md) - Detailed monitoring guide
715
- - [Custom Transports](custom-transports.md) - How transports interact with the dispatcher
716
- - [Troubleshooting](troubleshooting.md) - Common dispatcher issues
716
+ - [Troubleshooting](../development/troubleshooting.md) - Common dispatcher issues
@@ -320,7 +320,7 @@ class FilterTest < Minitest::Test
320
320
  @transport = SmartMessage::Transport.create(:memory, auto_process: true)
321
321
  TestMessage.config do
322
322
  transport @transport
323
- serializer SmartMessage::Serializer::JSON.new
323
+ serializer SmartMessage::Serializer::Json.new
324
324
  end
325
325
  TestMessage.unsubscribe!
326
326
  end
@@ -447,5 +447,4 @@ end
447
447
 
448
448
  - [Dispatcher Documentation](dispatcher.md) - How filtering integrates with message routing
449
449
  - [Entity Addressing](addressing.md) - Understanding `from`, `to`, and `reply_to` fields
450
- - [Examples](examples.md) - Complete working examples with filtering
451
- - [Testing Guide](testing.md) - Best practices for testing filtered subscriptions
450
+ - [Examples](../getting-started/examples.md) - Complete working examples with filtering
@@ -36,9 +36,9 @@ SmartMessage supports multiple ways to handle incoming messages:
36
36
  ### 1. Default Handler Pattern (using `self.process`)
37
37
  ```ruby
38
38
  class SensorDataMessage < SmartMessage::Base
39
- def self.process(message_header, message_payload)
39
+ def self.process(decoded_message)
40
40
  # This gets called when a SensorDataMessage is received
41
- data = JSON.parse(message_payload)
41
+ # decoded_message is already a message instance
42
42
  puts "Sensor reading: #{data['value']}"
43
43
  end
44
44
  end
@@ -51,7 +51,7 @@ SensorDataMessage.subscribe # Uses "SensorDataMessage.process"
51
51
  class ThermostatService
52
52
  def self.handle_sensor_data(message_header, message_payload)
53
53
  # Custom processing logic
54
- data = JSON.parse(message_payload)
54
+ # decoded_message is already a message instance
55
55
  adjust_temperature(data)
56
56
  end
57
57
  end
@@ -99,8 +99,8 @@ Looking at the smart home IoT example:
99
99
 
100
100
  ```ruby
101
101
  class SensorDataMessage < SmartMessage::Base
102
- def self.process(message_header, message_payload)
103
- sensor_data = JSON.parse(message_payload)
102
+ def self.process(decoded_message)
103
+ sensor_# decoded_message is already a message instance
104
104
  icon = case sensor_data['device_type']
105
105
  when 'thermostat' then '🌡️'
106
106
  when 'security_camera' then '📹'
@@ -145,8 +145,8 @@ A single message type can have multiple subscribers with different handlers usin
145
145
  ```ruby
146
146
  # Default handler for logging
147
147
  class SensorDataMessage < SmartMessage::Base
148
- def self.process(message_header, message_payload)
149
- data = JSON.parse(message_payload)
148
+ def self.process(decoded_message)
149
+ # decoded_message is already a message instance
150
150
  puts "📊 Sensor data logged: #{data['device_id']}"
151
151
  end
152
152
  end
@@ -154,7 +154,7 @@ end
154
154
  # Custom method handler for specific services
155
155
  class ThermostatService
156
156
  def self.handle_sensor_data(message_header, message_payload)
157
- data = JSON.parse(message_payload)
157
+ # decoded_message is already a message instance
158
158
  return unless data['device_type'] == 'thermostat'
159
159
  adjust_temperature(data['value'])
160
160
  end
@@ -197,10 +197,10 @@ SensorDataMessage.subscribe(database_logger) # Proc handler
197
197
 
198
198
  ```ruby
199
199
  class SensorDataMessage < SmartMessage::Base
200
- def self.process(message_header, message_payload)
200
+ def self.process(decoded_message)
201
201
  # This runs in its own thread
202
202
  # Be careful with shared state
203
- data = JSON.parse(message_payload)
203
+ # decoded_message is already a message instance
204
204
 
205
205
  # Thread-safe operations
206
206
  update_local_cache(data)
@@ -216,9 +216,9 @@ Handlers should include proper error handling:
216
216
 
217
217
  ```ruby
218
218
  class SensorDataMessage < SmartMessage::Base
219
- def self.process(message_header, message_payload)
219
+ def self.process(decoded_message)
220
220
  begin
221
- data = JSON.parse(message_payload)
221
+ # decoded_message is already a message instance
222
222
 
223
223
  # Validate required fields
224
224
  raise "Missing device_id" unless data['device_id']
@@ -310,9 +310,9 @@ PaymentEventMessage.subscribe(audit_logger)
310
310
 
311
311
  ### 1. Keep Handlers Fast
312
312
  ```ruby
313
- def self.process(message_header, message_payload)
313
+ def self.process(decoded_message)
314
314
  # Quick validation
315
- data = JSON.parse(message_payload)
315
+ # decoded_message is already a message instance
316
316
  return unless valid_message?(data)
317
317
 
318
318
  # Delegate heavy work to background jobs
@@ -348,7 +348,7 @@ SensorDataMessage.subscribe do |h, p|; process_stuff(p); end
348
348
  ### 3. Filter Messages Early
349
349
  ```ruby
350
350
  def self.handle_thermostat_data(message_header, message_payload)
351
- data = JSON.parse(message_payload)
351
+ # decoded_message is already a message instance
352
352
 
353
353
  # Filter early to avoid unnecessary processing
354
354
  return unless data['device_type'] == 'thermostat'
@@ -361,11 +361,11 @@ end
361
361
 
362
362
  ### 4. Include Logging and Monitoring
363
363
  ```ruby
364
- def self.process(message_header, message_payload)
364
+ def self.process(decoded_message)
365
365
  start_time = Time.now
366
366
 
367
367
  begin
368
- data = JSON.parse(message_payload)
368
+ # decoded_message is already a message instance
369
369
  logger.info "Processing sensor data from #{data['device_id']}"
370
370
 
371
371
  # Business logic here