pigeon-rb 0.1.0

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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +343 -0
  3. data/lib/pigeon/active_job_integration.rb +32 -0
  4. data/lib/pigeon/api.rb +200 -0
  5. data/lib/pigeon/configuration.rb +161 -0
  6. data/lib/pigeon/core.rb +104 -0
  7. data/lib/pigeon/encryption.rb +213 -0
  8. data/lib/pigeon/generators/hanami/migration_generator.rb +89 -0
  9. data/lib/pigeon/generators/rails/install_generator.rb +32 -0
  10. data/lib/pigeon/generators/rails/migration_generator.rb +20 -0
  11. data/lib/pigeon/generators/rails/templates/create_outbox_messages.rb.erb +34 -0
  12. data/lib/pigeon/generators/rails/templates/initializer.rb.erb +88 -0
  13. data/lib/pigeon/hanami_integration.rb +78 -0
  14. data/lib/pigeon/health_check/kafka.rb +37 -0
  15. data/lib/pigeon/health_check/processor.rb +70 -0
  16. data/lib/pigeon/health_check/queue.rb +69 -0
  17. data/lib/pigeon/health_check.rb +63 -0
  18. data/lib/pigeon/logging/structured_logger.rb +181 -0
  19. data/lib/pigeon/metrics/collector.rb +200 -0
  20. data/lib/pigeon/mock_producer.rb +18 -0
  21. data/lib/pigeon/models/adapters/active_record_adapter.rb +133 -0
  22. data/lib/pigeon/models/adapters/rom_adapter.rb +150 -0
  23. data/lib/pigeon/models/outbox_message.rb +182 -0
  24. data/lib/pigeon/monitoring.rb +113 -0
  25. data/lib/pigeon/outbox.rb +61 -0
  26. data/lib/pigeon/processor/background_processor.rb +109 -0
  27. data/lib/pigeon/processor.rb +798 -0
  28. data/lib/pigeon/publisher.rb +524 -0
  29. data/lib/pigeon/railtie.rb +29 -0
  30. data/lib/pigeon/schema.rb +35 -0
  31. data/lib/pigeon/security.rb +30 -0
  32. data/lib/pigeon/serializer.rb +77 -0
  33. data/lib/pigeon/tasks/pigeon.rake +64 -0
  34. data/lib/pigeon/trace_api.rb +37 -0
  35. data/lib/pigeon/tracing/core.rb +119 -0
  36. data/lib/pigeon/tracing/messaging.rb +144 -0
  37. data/lib/pigeon/tracing.rb +107 -0
  38. data/lib/pigeon/version.rb +5 -0
  39. data/lib/pigeon.rb +52 -0
  40. metadata +127 -0
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pigeon
4
+ # Monitoring-related functionality for Pigeon
5
+ module Monitoring
6
+ # Get the metrics collector
7
+ # @return [Pigeon::Metrics::Collector] Metrics collector instance
8
+ def self.metrics_collector
9
+ Core.config.metrics_collector ||= Pigeon::Metrics::Collector.new
10
+ end
11
+
12
+ # Get all metrics
13
+ # @return [Hash] All metrics
14
+ def self.metrics
15
+ metrics_collector.all_metrics
16
+ end
17
+
18
+ # Get metrics in a format suitable for monitoring systems
19
+ # @return [Hash] Formatted metrics
20
+ def self.metrics_for_monitoring
21
+ metrics_collector.metrics_for_monitoring
22
+ end
23
+
24
+ # Reset all metrics
25
+ # @return [void]
26
+ def self.reset_metrics
27
+ metrics_collector.reset
28
+ end
29
+
30
+ # Create a logger with additional context
31
+ # @param context [Hash] Additional context to include in all log entries
32
+ # @return [Pigeon::Logging::StructuredLogger] Logger with context
33
+ def self.logger_with_context(context = {})
34
+ Core.config.logger.with_context(context)
35
+ end
36
+
37
+ # Set the log level
38
+ # @param level [Symbol, String, Integer] Log level
39
+ # @return [Integer] New log level
40
+ def self.log_level=(level)
41
+ Core.config.logger.level = level
42
+ end
43
+
44
+ # Get the current log level
45
+ # @return [Integer] Current log level
46
+ def self.log_level
47
+ Core.config.logger.level
48
+ end
49
+
50
+ # Get the time when the processor was started
51
+ # @return [Time, nil] Start time or nil if not started
52
+ def self.processor_start_time
53
+ @processor_start_time
54
+ end
55
+
56
+ # Get the time of the last processing run
57
+ # @return [Time, nil] Last run time or nil if never run
58
+ def self.last_processing_run
59
+ @last_processing_run
60
+ end
61
+
62
+ # Get the time of the last successful processing run
63
+ # @return [Time, nil] Last successful run time or nil if never run
64
+ def self.last_successful_processing_run
65
+ @last_successful_processing_run
66
+ end
67
+
68
+ # Set the processor start time
69
+ # @param time [Time] Start time
70
+ # @return [Time] Start time
71
+ def self.processor_start_time=(time)
72
+ @processor_start_time = time
73
+ end
74
+
75
+ # Set the time of the last processing run
76
+ # @param time [Time] Last run time
77
+ # @return [Time] Last run time
78
+ def self.last_processing_run=(time)
79
+ @last_processing_run = time
80
+ end
81
+
82
+ # Set the time of the last successful processing run
83
+ # @param time [Time] Last successful run time
84
+ # @return [Time] Last successful run time
85
+ def self.last_successful_processing_run=(time)
86
+ @last_successful_processing_run = time
87
+ end
88
+
89
+ # Get the health status of the processor
90
+ # @return [Hash] Health status
91
+ def self.processor_health
92
+ HealthCheck.processor_health
93
+ end
94
+
95
+ # Get the health status of the queue
96
+ # @return [Hash] Health status
97
+ def self.queue_health
98
+ HealthCheck.queue_health
99
+ end
100
+
101
+ # Get the health status of Kafka connectivity
102
+ # @return [Hash] Health status
103
+ def self.kafka_health
104
+ HealthCheck.kafka_health
105
+ end
106
+
107
+ # Get the overall health status
108
+ # @return [Hash] Overall health status
109
+ def self.health_status
110
+ HealthCheck.status
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pigeon
4
+ # Outbox-related functionality for Pigeon
5
+ module Outbox
6
+ # Get the appropriate outbox message adapter based on the framework
7
+ # @return [Class] Adapter class
8
+ def self.outbox_message_adapter
9
+ if defined?(ActiveRecord)
10
+ Models::Adapters::ActiveRecordAdapter
11
+ elsif defined?(ROM) && defined?(Hanami)
12
+ Models::Adapters::RomAdapter
13
+ else
14
+ Models::OutboxMessage
15
+ end
16
+ end
17
+
18
+ # Create a new outbox message
19
+ # @param attributes [Hash] Message attributes
20
+ # @return [Pigeon::Models::OutboxMessage] New message instance
21
+ def self.create_outbox_message(attributes = {})
22
+ outbox_message_adapter.create(attributes)
23
+ end
24
+
25
+ # Find an outbox message by ID
26
+ # @param id [String, Integer] Message ID
27
+ # @return [Pigeon::Models::OutboxMessage, nil] Message instance or nil if not found
28
+ def self.find_outbox_message(id)
29
+ outbox_message_adapter.find(id)
30
+ end
31
+
32
+ # Find outbox messages by status
33
+ # @param status [String] Message status
34
+ # @param limit [Integer] Maximum number of messages to return
35
+ # @return [Array<Pigeon::Models::OutboxMessage>] Array of message instances
36
+ def self.find_outbox_messages_by_status(status, limit = 100)
37
+ outbox_message_adapter.find_by_status(status, limit)
38
+ end
39
+
40
+ # Find outbox messages ready for retry
41
+ # @param limit [Integer] Maximum number of messages to return
42
+ # @return [Array<Pigeon::Models::OutboxMessage>] Array of message instances
43
+ def self.find_outbox_messages_ready_for_retry(limit = 100)
44
+ outbox_message_adapter.find_ready_for_retry(limit)
45
+ end
46
+
47
+ # Count outbox messages by status
48
+ # @param status [String] Message status
49
+ # @return [Integer] Count of messages with the given status
50
+ def self.count_outbox_messages_by_status(status)
51
+ outbox_message_adapter.count_by_status(status)
52
+ end
53
+
54
+ # Find the oldest outbox message by status
55
+ # @param status [String] Message status
56
+ # @return [Pigeon::Models::OutboxMessage, nil] Oldest message or nil if none found
57
+ def self.find_oldest_outbox_message_by_status(status)
58
+ outbox_message_adapter.find_oldest_by_status(status)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent"
4
+
5
+ module Pigeon
6
+ class Processor
7
+ # Background processing functionality for the processor
8
+ module BackgroundProcessor
9
+ # Start processing pending messages in the background
10
+ # @param batch_size [Integer] Number of messages to process in one batch
11
+ # @param interval [Integer] Interval in seconds between processing batches
12
+ # @param thread_count [Integer] Number of threads to use for processing
13
+ # @return [Boolean] Whether processing was started
14
+ def start_background_processing(batch_size: 100, interval: 5, thread_count: 2)
15
+ return false if @processing.true?
16
+
17
+ @mutex.with_write_lock do
18
+ return false if @processing.true?
19
+
20
+ Pigeon.config.logger.info("Starting background processing of outbox messages")
21
+
22
+ # Process any pending messages immediately
23
+ process_startup_messages(batch_size)
24
+
25
+ # Create a thread pool for processing
26
+ @thread_pool = Concurrent::FixedThreadPool.new(thread_count)
27
+
28
+ # Set up periodic processing
29
+ @scheduler = Concurrent::TimerTask.new(execution_interval: interval) do
30
+ process_pending(batch_size: batch_size)
31
+ end
32
+
33
+ # Start the scheduler
34
+ @scheduler.execute
35
+
36
+ @processing.make_true
37
+ end
38
+
39
+ true
40
+ end
41
+
42
+ # Stop processing pending messages
43
+ # @return [Boolean] Whether processing was stopped
44
+ def stop_background_processing
45
+ return false unless @processing.true?
46
+
47
+ @mutex.with_write_lock do
48
+ return false unless @processing.true?
49
+
50
+ Pigeon.config.logger.info("Stopping background processing of outbox messages")
51
+
52
+ # Stop the scheduler
53
+ @scheduler&.shutdown
54
+
55
+ # Shutdown the thread pool
56
+ @thread_pool&.shutdown
57
+ @thread_pool&.wait_for_termination(10) # Wait up to 10 seconds for threads to finish
58
+
59
+ @processing.make_false
60
+ end
61
+
62
+ true
63
+ end
64
+
65
+ # Check if processing is running
66
+ # @return [Boolean] Whether processing is running
67
+ def processing?
68
+ @processing.true?
69
+ end
70
+
71
+ # Process pending messages on startup
72
+ # @param batch_size [Integer] Number of messages to process in one batch
73
+ # @return [Hash] Processing statistics
74
+ def process_startup_messages(batch_size = 100)
75
+ Pigeon.config.logger.info("Processing pending messages on startup")
76
+
77
+ # Process pending messages
78
+ stats = process_pending(batch_size: batch_size)
79
+
80
+ # Log the results
81
+ Pigeon.config.logger.info("Startup processing complete: #{stats[:processed]} messages processed")
82
+
83
+ stats
84
+ end
85
+
86
+ private
87
+
88
+ # Process messages in thread pool or synchronously
89
+ # @param messages [Array<Pigeon::Models::OutboxMessage>] Messages to process
90
+ # @param stats [Hash] Statistics hash to update
91
+ # @return [void]
92
+ def process_messages_batch(messages, stats)
93
+ messages.each do |message|
94
+ if @thread_pool && @processing.true?
95
+ # Process in thread pool if available
96
+ @thread_pool.post do
97
+ process_result = process_single_message(message)
98
+ update_stats(stats, process_result)
99
+ end
100
+ else
101
+ # Process synchronously
102
+ process_result = process_single_message(message)
103
+ update_stats(stats, process_result)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end