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.
- checksums.yaml +7 -0
- data/README.md +343 -0
- data/lib/pigeon/active_job_integration.rb +32 -0
- data/lib/pigeon/api.rb +200 -0
- data/lib/pigeon/configuration.rb +161 -0
- data/lib/pigeon/core.rb +104 -0
- data/lib/pigeon/encryption.rb +213 -0
- data/lib/pigeon/generators/hanami/migration_generator.rb +89 -0
- data/lib/pigeon/generators/rails/install_generator.rb +32 -0
- data/lib/pigeon/generators/rails/migration_generator.rb +20 -0
- data/lib/pigeon/generators/rails/templates/create_outbox_messages.rb.erb +34 -0
- data/lib/pigeon/generators/rails/templates/initializer.rb.erb +88 -0
- data/lib/pigeon/hanami_integration.rb +78 -0
- data/lib/pigeon/health_check/kafka.rb +37 -0
- data/lib/pigeon/health_check/processor.rb +70 -0
- data/lib/pigeon/health_check/queue.rb +69 -0
- data/lib/pigeon/health_check.rb +63 -0
- data/lib/pigeon/logging/structured_logger.rb +181 -0
- data/lib/pigeon/metrics/collector.rb +200 -0
- data/lib/pigeon/mock_producer.rb +18 -0
- data/lib/pigeon/models/adapters/active_record_adapter.rb +133 -0
- data/lib/pigeon/models/adapters/rom_adapter.rb +150 -0
- data/lib/pigeon/models/outbox_message.rb +182 -0
- data/lib/pigeon/monitoring.rb +113 -0
- data/lib/pigeon/outbox.rb +61 -0
- data/lib/pigeon/processor/background_processor.rb +109 -0
- data/lib/pigeon/processor.rb +798 -0
- data/lib/pigeon/publisher.rb +524 -0
- data/lib/pigeon/railtie.rb +29 -0
- data/lib/pigeon/schema.rb +35 -0
- data/lib/pigeon/security.rb +30 -0
- data/lib/pigeon/serializer.rb +77 -0
- data/lib/pigeon/tasks/pigeon.rake +64 -0
- data/lib/pigeon/trace_api.rb +37 -0
- data/lib/pigeon/tracing/core.rb +119 -0
- data/lib/pigeon/tracing/messaging.rb +144 -0
- data/lib/pigeon/tracing.rb +107 -0
- data/lib/pigeon/version.rb +5 -0
- data/lib/pigeon.rb +52 -0
- 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
|