smart_message 0.0.8 โ†’ 0.0.9

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.irbrc +24 -0
  4. data/CHANGELOG.md +96 -0
  5. data/Gemfile.lock +6 -1
  6. data/README.md +289 -15
  7. data/docs/README.md +3 -1
  8. data/docs/addressing.md +119 -13
  9. data/docs/architecture.md +68 -0
  10. data/docs/dead_letter_queue.md +673 -0
  11. data/docs/dispatcher.md +87 -0
  12. data/docs/examples.md +59 -1
  13. data/docs/getting-started.md +8 -1
  14. data/docs/logging.md +382 -326
  15. data/docs/message_filtering.md +451 -0
  16. data/examples/01_point_to_point_orders.rb +54 -53
  17. data/examples/02_publish_subscribe_events.rb +14 -10
  18. data/examples/03_many_to_many_chat.rb +16 -8
  19. data/examples/04_redis_smart_home_iot.rb +20 -10
  20. data/examples/05_proc_handlers.rb +12 -11
  21. data/examples/06_custom_logger_example.rb +95 -100
  22. data/examples/07_error_handling_scenarios.rb +4 -2
  23. data/examples/08_entity_addressing_basic.rb +18 -6
  24. data/examples/08_entity_addressing_with_filtering.rb +27 -9
  25. data/examples/09_dead_letter_queue_demo.rb +559 -0
  26. data/examples/09_regex_filtering_microservices.rb +407 -0
  27. data/examples/10_header_block_configuration.rb +263 -0
  28. data/examples/11_global_configuration_example.rb +219 -0
  29. data/examples/README.md +102 -0
  30. data/examples/dead_letters.jsonl +12 -0
  31. data/examples/performance_metrics/benchmark_results_ractor_20250818_205603.json +135 -0
  32. data/examples/performance_metrics/benchmark_results_ractor_20250818_205831.json +135 -0
  33. data/examples/performance_metrics/benchmark_results_test_20250818_204942.json +130 -0
  34. data/examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json +130 -0
  35. data/examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json +130 -0
  36. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json +130 -0
  37. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json +130 -0
  38. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json +130 -0
  39. data/examples/performance_metrics/benchmark_results_unknown_20250819_172852.json +130 -0
  40. data/examples/performance_metrics/compare_benchmarks.rb +519 -0
  41. data/examples/performance_metrics/dead_letters.jsonl +3100 -0
  42. data/examples/performance_metrics/performance_benchmark.rb +344 -0
  43. data/examples/show_logger.rb +367 -0
  44. data/examples/show_me.rb +145 -0
  45. data/examples/temp.txt +94 -0
  46. data/examples/tmux_chat/bot_agent.rb +4 -2
  47. data/examples/tmux_chat/human_agent.rb +4 -2
  48. data/examples/tmux_chat/room_monitor.rb +4 -2
  49. data/examples/tmux_chat/shared_chat_system.rb +6 -3
  50. data/lib/smart_message/addressing.rb +259 -0
  51. data/lib/smart_message/base.rb +121 -599
  52. data/lib/smart_message/circuit_breaker.rb +2 -1
  53. data/lib/smart_message/configuration.rb +199 -0
  54. data/lib/smart_message/dead_letter_queue.rb +27 -10
  55. data/lib/smart_message/dispatcher.rb +90 -49
  56. data/lib/smart_message/header.rb +5 -0
  57. data/lib/smart_message/logger/base.rb +21 -1
  58. data/lib/smart_message/logger/default.rb +88 -138
  59. data/lib/smart_message/logger/lumberjack.rb +324 -0
  60. data/lib/smart_message/logger/null.rb +81 -0
  61. data/lib/smart_message/logger.rb +17 -9
  62. data/lib/smart_message/messaging.rb +100 -0
  63. data/lib/smart_message/plugins.rb +132 -0
  64. data/lib/smart_message/serializer/base.rb +25 -8
  65. data/lib/smart_message/serializer/json.rb +5 -4
  66. data/lib/smart_message/subscription.rb +193 -0
  67. data/lib/smart_message/transport/base.rb +72 -41
  68. data/lib/smart_message/transport/memory_transport.rb +7 -5
  69. data/lib/smart_message/transport/redis_transport.rb +15 -45
  70. data/lib/smart_message/transport/stdout_transport.rb +18 -8
  71. data/lib/smart_message/transport.rb +1 -34
  72. data/lib/smart_message/utilities.rb +142 -0
  73. data/lib/smart_message/version.rb +1 -1
  74. data/lib/smart_message/versioning.rb +85 -0
  75. data/lib/smart_message/wrapper.rb.bak +132 -0
  76. data/lib/smart_message.rb +74 -28
  77. data/smart_message.gemspec +3 -0
  78. metadata +76 -3
  79. data/lib/smart_message/serializer.rb +0 -10
  80. data/lib/smart_message/wrapper.rb +0 -43
@@ -0,0 +1,559 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+
5
+ # examples/09_dead_letter_queue_demo.rb
6
+ #
7
+ # Demonstrates the Dead Letter Queue (DLQ) system with:
8
+ # - Automatic capture via circuit breakers
9
+ # - Manual capture of failed messages
10
+ # - Replay capabilities
11
+ # - Administrative functions
12
+ # - Monitoring and statistics
13
+
14
+ require_relative '../lib/smart_message'
15
+ require 'json'
16
+ require 'fileutils'
17
+
18
+ # Configure DLQ for this demo
19
+ DEMO_DLQ_PATH = '/tmp/smart_message_dlq_demo.jsonl'
20
+ SmartMessage::DeadLetterQueue.configure_default(DEMO_DLQ_PATH)
21
+
22
+ # Clean up any existing DLQ file from previous runs
23
+ FileUtils.rm_f(DEMO_DLQ_PATH)
24
+
25
+ puts "=" * 80
26
+ puts "SmartMessage Dead Letter Queue Demo"
27
+ puts "=" * 80
28
+ puts "\nDLQ Path: #{DEMO_DLQ_PATH}"
29
+ puts "=" * 80
30
+
31
+ # ==============================================================================
32
+ # 1. Define Message Classes
33
+ # ==============================================================================
34
+
35
+ class PaymentMessage < SmartMessage::Base
36
+ description "Payment processing message that might fail"
37
+ version 1
38
+ from 'payment-service'
39
+ to 'bank-gateway'
40
+
41
+ property :payment_id, required: true, description: "Unique payment identifier"
42
+ property :amount, required: true, description: "Payment amount"
43
+ property :customer_id, required: true, description: "Customer making payment"
44
+ property :card_last_four, description: "Last 4 digits of card"
45
+
46
+ config do
47
+ transport SmartMessage::Transport.create(:memory)
48
+ serializer SmartMessage::Serializer::JSON.new
49
+ end
50
+
51
+ def self.process(wrapper)
52
+ header = wrapper._sm_header
53
+ payload = wrapper._sm_payload
54
+ data = JSON.parse(payload, symbolize_names: true)
55
+ puts " ๐Ÿ’ณ Processing payment #{data[:payment_id]} for $#{data[:amount]}"
56
+ end
57
+ end
58
+
59
+ class OrderMessage < SmartMessage::Base
60
+ description "Order processing message"
61
+ version 1
62
+ from 'order-service'
63
+ to 'fulfillment-service'
64
+
65
+ property :order_id, required: true, description: "Order identifier"
66
+ property :items, default: [], description: "Order items"
67
+ property :total, required: true, description: "Order total"
68
+
69
+ config do
70
+ transport SmartMessage::Transport.create(:memory)
71
+ serializer SmartMessage::Serializer::JSON.new
72
+ end
73
+
74
+ def self.process(wrapper)
75
+ header = wrapper._sm_header
76
+ payload = wrapper._sm_payload
77
+ data = JSON.parse(payload, symbolize_names: true)
78
+ puts " ๐Ÿ“ฆ Processing order #{data[:order_id]} with #{data[:items].size} items"
79
+ end
80
+ end
81
+
82
+ class NotificationMessage < SmartMessage::Base
83
+ description "User notification message"
84
+ version 1
85
+ from 'notification-service'
86
+
87
+ property :user_id, required: true, description: "User to notify"
88
+ property :message, required: true, description: "Notification content"
89
+ property :channel, default: 'email', description: "Notification channel"
90
+
91
+ config do
92
+ transport SmartMessage::Transport.create(:memory)
93
+ serializer SmartMessage::Serializer::JSON.new
94
+ end
95
+
96
+ def self.process(wrapper)
97
+ header = wrapper._sm_header
98
+ payload = wrapper._sm_payload
99
+ data = JSON.parse(payload, symbolize_names: true)
100
+ puts " ๐Ÿ“ง Sending #{data[:channel]} to user #{data[:user_id]}: #{data[:message]}"
101
+ end
102
+ end
103
+
104
+ # ==============================================================================
105
+ # 2. Simulate Transport Failures
106
+ # ==============================================================================
107
+
108
+ class FailingTransport < SmartMessage::Transport::Base
109
+ attr_accessor :failure_mode, :failure_count
110
+
111
+ def initialize(**options)
112
+ super
113
+ @failure_mode = options[:failure_mode] || :none
114
+ @failure_count = 0
115
+ @max_failures = options[:max_failures] || 3
116
+ end
117
+
118
+ def do_publish(message_header, message_payload)
119
+ case @failure_mode
120
+ when :connection_error
121
+ @failure_count += 1
122
+ if @failure_count <= @max_failures
123
+ raise "Connection refused to payment gateway"
124
+ end
125
+ puts " โœ… Transport recovered, message sent"
126
+ when :timeout
127
+ @failure_count += 1
128
+ if @failure_count <= @max_failures
129
+ raise "Request timeout after 30 seconds"
130
+ end
131
+ puts " โœ… Transport recovered, message sent"
132
+ when :intermittent
133
+ if rand < 0.5
134
+ raise "Intermittent network error"
135
+ end
136
+ puts " โœ… Message sent successfully"
137
+ else
138
+ puts " โœ… Message published successfully"
139
+ end
140
+ end
141
+ end
142
+
143
+ # ==============================================================================
144
+ # 3. Demo Functions
145
+ # ==============================================================================
146
+
147
+ def section_header(title)
148
+ puts "\n" + "=" * 80
149
+ puts "#{title}"
150
+ puts "=" * 80
151
+ end
152
+
153
+ def show_dlq_status
154
+ dlq = SmartMessage::DeadLetterQueue.default
155
+ puts "\n๐Ÿ“Š DLQ Status:"
156
+ puts " - Queue size: #{dlq.size} messages"
157
+
158
+ if dlq.size > 0
159
+ stats = dlq.statistics
160
+ puts " - By class: #{stats[:by_class]}"
161
+ puts " - By error: #{stats[:by_error].keys.first(3).join(', ')}..."
162
+
163
+ # Show next message
164
+ next_msg = dlq.peek
165
+ if next_msg
166
+ puts " - Next message: #{next_msg[:header][:message_class]} (#{next_msg[:error]})"
167
+ end
168
+ end
169
+ end
170
+
171
+ # ==============================================================================
172
+ # 4. Demonstration Scenarios
173
+ # ==============================================================================
174
+
175
+ section_header("1. MANUAL DLQ CAPTURE")
176
+ puts "\nManually capturing failed messages in DLQ..."
177
+
178
+ dlq = SmartMessage::DeadLetterQueue.default
179
+
180
+ # Simulate validation failure
181
+ begin
182
+ payment = PaymentMessage.new(
183
+ payment_id: "PAY-001",
184
+ amount: -100, # Invalid amount
185
+ customer_id: "CUST-123"
186
+ )
187
+
188
+ # Business logic validation
189
+ if payment.amount < 0
190
+ raise "Invalid payment amount: cannot be negative"
191
+ end
192
+ rescue => e
193
+ puts "โŒ Validation failed: #{e.message}"
194
+
195
+ # Manually add to DLQ
196
+ wrapper = SmartMessage::Wrapper::Base.new(
197
+ header: payment._sm_header,
198
+ payload: payment.encode
199
+ )
200
+ dlq.enqueue(
201
+ wrapper,
202
+ error: e.message,
203
+ transport: "ValidationLayer",
204
+ retry_count: 0
205
+ )
206
+ puts "๐Ÿ’พ Message saved to DLQ"
207
+ end
208
+
209
+ show_dlq_status
210
+
211
+ # ==============================================================================
212
+
213
+ section_header("2. AUTOMATIC CAPTURE VIA TRANSPORT FAILURE")
214
+ puts "\nSimulating transport failures that trigger DLQ..."
215
+
216
+ # Create a payment with failing transport
217
+ payment = PaymentMessage.new(
218
+ payment_id: "PAY-002",
219
+ amount: 250.00,
220
+ customer_id: "CUST-456",
221
+ card_last_four: "1234"
222
+ )
223
+
224
+ # Override with failing transport
225
+ failing_transport = FailingTransport.new(
226
+ failure_mode: :connection_error,
227
+ max_failures: 2
228
+ )
229
+ payment.transport(failing_transport)
230
+
231
+ # First attempt - will fail
232
+ puts "\n๐Ÿ”„ Attempt 1: Publishing payment message..."
233
+ begin
234
+ payment.publish
235
+ rescue => e
236
+ puts "โŒ Publish failed: #{e.message}"
237
+
238
+ # Circuit breaker would normally handle this, but we'll add manually for demo
239
+ wrapper = SmartMessage::Wrapper::Base.new(
240
+ header: payment._sm_header,
241
+ payload: payment.encode
242
+ )
243
+ dlq.enqueue(
244
+ wrapper,
245
+ error: e.message,
246
+ transport: failing_transport.class.name,
247
+ retry_count: 1
248
+ )
249
+ puts "๐Ÿ’พ Message automatically saved to DLQ"
250
+ end
251
+
252
+ show_dlq_status
253
+
254
+ # ==============================================================================
255
+
256
+ section_header("3. MULTIPLE FAILURE TYPES")
257
+ puts "\nAdding various types of failures to DLQ..."
258
+
259
+ # Order with timeout
260
+ order = OrderMessage.new(
261
+ order_id: "ORD-789",
262
+ items: ["SKU-001", "SKU-002"],
263
+ total: 99.99
264
+ )
265
+
266
+ wrapper = SmartMessage::Wrapper::Base.new(
267
+ header: order._sm_header,
268
+ payload: order.encode
269
+ )
270
+ dlq.enqueue(
271
+ wrapper,
272
+ error: "Gateway timeout after 30 seconds",
273
+ transport: "Redis",
274
+ retry_count: 3
275
+ )
276
+ puts "๐Ÿ’พ Added order with timeout error"
277
+
278
+ # Notification with rate limit
279
+ notification = NotificationMessage.new(
280
+ user_id: "USER-999",
281
+ message: "Your order has shipped!",
282
+ channel: "sms"
283
+ )
284
+
285
+ wrapper = SmartMessage::Wrapper::Base.new(
286
+ header: notification._sm_header,
287
+ payload: notification.encode
288
+ )
289
+ dlq.enqueue(
290
+ wrapper,
291
+ error: "Rate limit exceeded: 429 Too Many Requests",
292
+ transport: "SMSGateway",
293
+ retry_count: 1
294
+ )
295
+ puts "๐Ÿ’พ Added notification with rate limit error"
296
+
297
+ # Another payment with different error
298
+ payment3 = PaymentMessage.new(
299
+ payment_id: "PAY-003",
300
+ amount: 500.00,
301
+ customer_id: "CUST-789"
302
+ )
303
+
304
+ wrapper = SmartMessage::Wrapper::Base.new(
305
+ header: payment3._sm_header,
306
+ payload: payment3.encode
307
+ )
308
+ dlq.enqueue(
309
+ wrapper,
310
+ error: "Invalid merchant credentials",
311
+ transport: "StripeGateway",
312
+ retry_count: 0
313
+ )
314
+ puts "๐Ÿ’พ Added payment with auth error"
315
+
316
+ show_dlq_status
317
+
318
+ # ==============================================================================
319
+
320
+ section_header("4. FILTERING AND ANALYSIS")
321
+ puts "\nAnalyzing failed messages in DLQ..."
322
+
323
+ # Filter by message class
324
+ puts "\n๐Ÿ” Filtering by message class:"
325
+ payment_failures = dlq.filter_by_class('PaymentMessage')
326
+ puts " - Found #{payment_failures.size} failed PaymentMessage(s)"
327
+ payment_failures.each do |entry|
328
+ puts " โ€ข #{entry[:header][:uuid][0..7]}... - #{entry[:error]}"
329
+ end
330
+
331
+ order_failures = dlq.filter_by_class('OrderMessage')
332
+ puts " - Found #{order_failures.size} failed OrderMessage(s)"
333
+
334
+ # Filter by error pattern
335
+ puts "\n๐Ÿ” Filtering by error pattern:"
336
+ timeout_errors = dlq.filter_by_error_pattern(/timeout/i)
337
+ puts " - Found #{timeout_errors.size} timeout error(s)"
338
+ timeout_errors.each do |entry|
339
+ puts " โ€ข #{entry[:header][:message_class]} - #{entry[:error]}"
340
+ end
341
+
342
+ connection_errors = dlq.filter_by_error_pattern(/connection|refused/i)
343
+ puts " - Found #{connection_errors.size} connection error(s)"
344
+
345
+ # Get detailed statistics
346
+ puts "\n๐Ÿ“Š Detailed Statistics:"
347
+ stats = dlq.statistics
348
+ puts " Total messages: #{stats[:total]}"
349
+ puts "\n By message class:"
350
+ stats[:by_class].each do |klass, count|
351
+ percentage = (count.to_f / stats[:total] * 100).round(1)
352
+ puts " โ€ข #{klass}: #{count} (#{percentage}%)"
353
+ end
354
+ puts "\n By error type:"
355
+ stats[:by_error].sort_by { |_, count| -count }.each do |error, count|
356
+ puts " โ€ข #{error[0..50]}#{'...' if error.length > 50}: #{count}"
357
+ end
358
+
359
+ # ==============================================================================
360
+
361
+ section_header("5. MESSAGE INSPECTION")
362
+ puts "\nInspecting messages without removing them..."
363
+
364
+ messages = dlq.inspect_messages(limit: 3)
365
+ messages.each_with_index do |msg, i|
366
+ puts "\n๐Ÿ“‹ Message #{i + 1}:"
367
+ puts " - Class: #{msg[:header][:message_class]}"
368
+ puts " - ID: #{msg[:header][:uuid][0..12]}..."
369
+ puts " - Error: #{msg[:error]}"
370
+ puts " - Retry count: #{msg[:retry_count]}"
371
+ puts " - Timestamp: #{msg[:timestamp]}"
372
+ end
373
+
374
+ # ==============================================================================
375
+
376
+ section_header("6. REPLAY CAPABILITIES")
377
+ puts "\nDemonstrating message replay functionality..."
378
+
379
+ puts "\n๐Ÿ”„ Replaying oldest message:"
380
+ puts " Queue size before: #{dlq.size}"
381
+
382
+ # Create a working transport for replay
383
+ working_transport = SmartMessage::Transport.create(:memory)
384
+
385
+ # Replay the oldest message
386
+ result = dlq.replay_one(working_transport)
387
+ if result[:success]
388
+ puts " โœ… Message replayed successfully!"
389
+ message = result[:message]
390
+ puts " โ€ข Class: #{message.class.name}"
391
+ puts " โ€ข Sent via: #{working_transport.class.name}"
392
+ else
393
+ puts " โŒ Replay failed: #{result[:error]}"
394
+ end
395
+
396
+ puts " Queue size after: #{dlq.size}"
397
+
398
+ # Batch replay
399
+ puts "\n๐Ÿ”„ Replaying batch of 2 messages:"
400
+ results = dlq.replay_batch(2, working_transport)
401
+ puts " โœ… Successfully replayed: #{results[:success]}"
402
+ puts " โŒ Failed to replay: #{results[:failed]}"
403
+ if results[:errors].any?
404
+ results[:errors].each do |error|
405
+ puts " โ€ข Error: #{error}"
406
+ end
407
+ end
408
+
409
+ show_dlq_status
410
+
411
+ # ==============================================================================
412
+
413
+ section_header("7. TIME-BASED OPERATIONS")
414
+ puts "\nExporting messages by time range..."
415
+
416
+ # Get all messages from the last minute
417
+ one_minute_ago = Time.now - 60
418
+ now = Time.now
419
+
420
+ recent_messages = dlq.export_range(one_minute_ago, now)
421
+ puts "๐Ÿ“… Messages from last minute: #{recent_messages.size}"
422
+
423
+ # Group by time intervals
424
+ if recent_messages.any?
425
+ puts "\n Timeline:"
426
+ recent_messages.each do |msg|
427
+ time = Time.parse(msg[:timestamp])
428
+ seconds_ago = (now - time).to_i
429
+ puts " โ€ข #{seconds_ago}s ago: #{msg[:header][:message_class]} - #{msg[:error][0..30]}..."
430
+ end
431
+ end
432
+
433
+ # ==============================================================================
434
+
435
+ section_header("8. ADMINISTRATIVE OPERATIONS")
436
+ puts "\nDemonstrating administrative functions..."
437
+
438
+ # Check current size
439
+ puts "\n๐Ÿ—„๏ธ Current DLQ state:"
440
+ puts " - File path: #{dlq.file_path}"
441
+ puts " - File exists: #{File.exist?(dlq.file_path)}"
442
+ puts " - File size: #{File.size(dlq.file_path)} bytes" if File.exist?(dlq.file_path)
443
+ puts " - Message count: #{dlq.size}"
444
+
445
+ # Peek at next message for processing
446
+ puts "\n๐Ÿ‘€ Peeking at next message (without removing):"
447
+ next_msg = dlq.peek
448
+ if next_msg
449
+ puts " - Would process: #{next_msg[:header][:message_class]}"
450
+ puts " - Error was: #{next_msg[:error]}"
451
+ puts " - Retry count: #{next_msg[:retry_count]}"
452
+ end
453
+
454
+ # Demonstrate FIFO order
455
+ puts "\n๐Ÿ“š FIFO Queue Order (dequeue order):"
456
+ temp_messages = []
457
+ 3.times do |i|
458
+ msg = dlq.dequeue
459
+ break unless msg
460
+ temp_messages << msg
461
+ puts " #{i + 1}. #{msg[:header][:message_class]} - #{msg[:timestamp]}"
462
+ end
463
+
464
+ # Put them back for other demos
465
+ temp_messages.each do |msg|
466
+ header = SmartMessage::Header.new(msg[:header])
467
+ wrapper = SmartMessage::Wrapper::Base.new(
468
+ header: header,
469
+ payload: msg[:payload]
470
+ )
471
+ dlq.enqueue(wrapper,
472
+ error: msg[:error],
473
+ retry_count: msg[:retry_count])
474
+ end
475
+
476
+ # ==============================================================================
477
+
478
+ section_header("9. CIRCUIT BREAKER INTEGRATION")
479
+ puts "\nDemonstrating circuit breaker with DLQ fallback..."
480
+
481
+ # This would normally be automatic, but we'll simulate it
482
+ class PaymentGateway
483
+ include BreakerMachines::DSL
484
+
485
+ circuit :payment_processing do
486
+ threshold failures: 2, within: 30.seconds
487
+ reset_after 10.seconds
488
+
489
+ # DLQ fallback
490
+ fallback do |exception|
491
+ puts " โšก Circuit breaker tripped: #{exception.message}"
492
+ # In real scenario, message would go to DLQ here
493
+ { circuit_open: true, error: exception.message }
494
+ end
495
+ end
496
+
497
+ def process_payment(payment_data)
498
+ circuit(:payment_processing).wrap do
499
+ # Simulate failure
500
+ raise "Payment gateway unavailable"
501
+ end
502
+ end
503
+ end
504
+
505
+ gateway = PaymentGateway.new
506
+ payment_data = { amount: 100.00, customer: "TEST" }
507
+
508
+ puts "Attempting payment processing with circuit breaker:"
509
+ 2.times do |i|
510
+ puts "\n Attempt #{i + 1}:"
511
+ result = gateway.process_payment(payment_data)
512
+ if result[:circuit_open]
513
+ puts " ๐Ÿ”ด Circuit is OPEN - request blocked"
514
+ # Message would be in DLQ now
515
+ end
516
+ end
517
+
518
+ # ==============================================================================
519
+
520
+ section_header("10. CLEANUP AND FINAL STATS")
521
+ puts "\nFinal DLQ statistics before cleanup..."
522
+
523
+ final_stats = dlq.statistics
524
+ puts "\n๐Ÿ“Š Final Statistics:"
525
+ puts " - Total messages processed: #{final_stats[:total]}"
526
+ puts " - Unique error types: #{final_stats[:by_error].keys.size}"
527
+ puts " - Message classes affected: #{final_stats[:by_class].keys.join(', ')}"
528
+
529
+ # Demonstrate clearing the queue
530
+ puts "\n๐Ÿงน Clearing the DLQ..."
531
+ puts " - Size before clear: #{dlq.size}"
532
+ dlq.clear
533
+ puts " - Size after clear: #{dlq.size}"
534
+
535
+ # Clean up demo file
536
+ FileUtils.rm_f(DEMO_DLQ_PATH)
537
+ puts "\nโœจ Demo complete! DLQ file cleaned up."
538
+
539
+ # ==============================================================================
540
+
541
+ puts "\n" + "=" * 80
542
+ puts "KEY TAKEAWAYS:"
543
+ puts "=" * 80
544
+ puts """
545
+ 1. DLQ automatically captures failed messages via circuit breakers
546
+ 2. Manual capture available for business logic failures
547
+ 3. Messages stored in JSON Lines format for efficiency
548
+ 4. FIFO queue ensures oldest failures processed first
549
+ 5. Replay capabilities with transport override support
550
+ 6. Rich filtering and analysis tools for debugging
551
+ 7. Time-based operations for historical analysis
552
+ 8. Thread-safe operations for production use
553
+ 9. Integration with circuit breakers for automatic capture
554
+ 10. Administrative tools for queue management
555
+
556
+ The Dead Letter Queue ensures no messages are lost during failures
557
+ and provides comprehensive tools for analysis and recovery.
558
+ """
559
+ puts "=" * 80