fourkites-sqspoller-v2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,53 @@
1
+ require "concurrent"
2
+ require "net/http"
3
+ require "rest-client"
4
+ require "json"
5
+ require "sqspoller/logger/logger"
6
+
7
+ # This handles the SQS message and forward to local puma server and get the response.
8
+ module SqsPoller
9
+ module Process
10
+ class MessageHandler
11
+
12
+ HEADERS = {
13
+ 'Content-Type' => 'application/json',
14
+ 'Accept' => 'application/json'
15
+ }
16
+
17
+ def initialize(worker_configuration)
18
+ @logger = SqsPoller::Logger.get_new_logger(self.class.name)
19
+ @http_method = worker_configuration[:http_method]
20
+ @http_url = worker_configuration[:http_url]
21
+ @timeout = worker_configuration[:timeout] ? worker_configuration[:timeout].to_i : 450
22
+ @uri = URI(@http_url)
23
+ end
24
+
25
+ def handle(message, message_id)
26
+ parsed_message = JSON.parse(message)
27
+ # @logger.info parsed_message
28
+ if @http_method.downcase == "post"
29
+ RestClient::Request.execute(:method => :post, :url => @http_url, :payload => parsed_message.to_json, :headers => HEADERS, :timeout => @timeout, :open_timeout => 5) do |response, request, result|
30
+ process_http_response response
31
+ end
32
+ elsif @http_method.downcase == "get"
33
+ RestClient::Request.execute(:method => :get, :url => @http_url, :payload => parsed_message.to_json, :headers => HEADERS, :timeout => @timeout, :open_timeout => 5) do |response, request, result|
34
+ process_http_response response
35
+ end
36
+ else
37
+ raise "Invalid http_method provided. #{http_method}"
38
+ end
39
+ parsed_message["MessageType"]
40
+ end
41
+
42
+ def process_http_response(response)
43
+ case response.code
44
+ when 200
45
+ return "OK"
46
+ else
47
+ raise "Service did not return 200 OK response. #{response.code}"
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,88 @@
1
+ require "logger"
2
+ require "concurrent"
3
+ require "net/http"
4
+ require "rest-client"
5
+ require "json"
6
+ require "sqspoller/logger/logger"
7
+ require "sqspoller/common/ring_buffer"
8
+ require "sqspoller/poll/queue_controller"
9
+
10
+ # TaskFinalizer will delete the batch of messages from SQS.
11
+ # It used the RingBuffer as the cache (Not persisted. We will lose all the messages if process killed or stopped.) to hold all completed tasks.
12
+ # When buffer filled the half the sizes it will try to delete the messages using sqs batch delete (max 10 per batch) from SQS and with max of 5 retries
13
+ module SqsPoller
14
+ module Process
15
+ class TaskFinalizer
16
+
17
+ DEFAULT_FINALIZE_TIMER_DELAY = 5.0
18
+ MAX_SQS_DELETE_BATCH_SIZE = 10
19
+ MAX_DELETE_RETRY = 5
20
+
21
+ def initialize(buffer_size)
22
+ @logger = SqsPoller::Logger.get_new_logger(self.class.name)
23
+ @buffer_size = buffer_size
24
+ @completed_tasks = SqsPoller::Common::RingBuffer.new(buffer_size * 2)
25
+ schedule_timer
26
+ end
27
+
28
+ def buffer_size
29
+ @buffer_size
30
+ end
31
+
32
+ def finalize(completed_task)
33
+ @completed_tasks.push completed_task
34
+ if @completed_tasks.count >= @buffer_size
35
+ @timer.reset
36
+ finalize_tasks
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def schedule_timer
43
+ @timer = Concurrent::ScheduledTask.new(DEFAULT_FINALIZE_TIMER_DELAY) {
44
+ finalize_tasks true
45
+ schedule_timer
46
+ }
47
+ @timer.execute
48
+ end
49
+
50
+ def finalize_tasks(scheduled = false)
51
+ return if !scheduled && @completed_tasks.count < @buffer_size
52
+ completed_tasks = @completed_tasks.flush
53
+ begin
54
+ task_group = completed_tasks.group_by { |task|
55
+ task[:queue_name]
56
+ }
57
+ task_group.each { |queue, tasks|
58
+ messages = tasks.map { |task|
59
+ task[:message]
60
+ }
61
+ messages.each_slice(MAX_SQS_DELETE_BATCH_SIZE) do |msgs|
62
+ SqsPoller::Poller::QueueController.delete_messages(queue, msgs)
63
+ end
64
+ @logger.debug "Deleted #{messages.size} messages from #{queue}"
65
+ }
66
+ rescue Exception => e
67
+ @logger.error "TaskFinalizer Caught error: #{e.message}, #{e.backtrace.join("\n")}"
68
+ finalize_task_individually(completed_tasks)
69
+ end
70
+ end
71
+
72
+ def finalize_task_individually(completed_tasks)
73
+ completed_tasks.each { |task|
74
+ begin
75
+ SqsPoller::Poller::QueueController.delete_message(task[:queue_name], task[:message])
76
+ rescue Exception => e
77
+ retry_delete = task[:retry_delete].present? ? task[:retry_delete] : 0
78
+ if retry_delete < MAX_DELETE_RETRY
79
+ task[:delete_retry] = retry_delete + 1
80
+ @completed_tasks.push task
81
+ end
82
+ end
83
+ }
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,50 @@
1
+ require "logger"
2
+ require "concurrent"
3
+ require "net/http"
4
+ require "sqspoller/worker_task"
5
+ require "sqspoller/common/ring_buffer"
6
+ require "sqspoller/process/task_finalizer"
7
+ require "sqspoller/common/utils"
8
+ require "sqspoller/metrics/sqs_poller_metrics"
9
+
10
+ # This Worker keep polls the task queue (SizedQueue with blocking) and delegate the message the message handler.
11
+ # Once message is process based on its status will send to task finalizer if the message is successfully handled.
12
+ # else it will ignore the message. Based on message visibility timeout in SQS it will reappear and processed.
13
+ module SqsPoller
14
+ module Process
15
+ class Worker
16
+
17
+ def initialize(worker_name, task_queue, task_finalizer, message_handler)
18
+ @worker_name = worker_name
19
+ @task_queue = task_queue
20
+ @message_handler = message_handler
21
+ @task_finalizer = task_finalizer
22
+ @logger = SqsPoller::Logger.get_new_logger("#{self.class.name}-#{@worker_name}")
23
+ end
24
+
25
+ def run
26
+ loop do
27
+ task = @task_queue.pop
28
+ message = task[:message]
29
+ success = false
30
+ timer = SqsPoller::Common::Utils.start_timer
31
+ message_type= "UNKNOWN"
32
+ begin
33
+ @logger.debug "Starting worker task for message: #{message.message_id}"
34
+ message_type = @message_handler.handle(message.body, message.message_id)
35
+ @logger.debug "Finished worker task for message: #{message.message_id}"
36
+ success = true
37
+ rescue Exception => e
38
+ @logger.error "Caught error: #{e.message}, #{e.backtrace.join("\n")} for message id: #{message.message_id}, body: #{message.body}"
39
+ end
40
+ elapsed_time = timer.stop
41
+ queue_wait_time = timer.start_time - task[:queue_time]
42
+ @logger.info "Task Completed queue_name: #{task[:queue_name]}, message_id: #{message.message_id}, message_type: #{message_type}, elapsed_time: #{elapsed_time}, queue_wait_time: #{queue_wait_time} message_count: #{task[:index]}"
43
+ SqsPoller::Metrics.record(task, success, timer, elapsed_time)
44
+ @task_finalizer.finalize(task) if success
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,81 @@
1
+ require "logger"
2
+ require "concurrent"
3
+ require "net/http"
4
+ require "sqspoller/worker_task"
5
+ require "sqspoller/common/ring_buffer"
6
+ require "sqspoller/process/task_finalizer"
7
+ require "sqspoller/common/utils"
8
+ require "sqspoller/metrics/sqs_poller_metrics"
9
+ require "sqspoller/process/task_worker"
10
+
11
+ # This manages all process task workers threads using threadpools.
12
+ # The number of thread can be configured in yaml config using concurrency:
13
+ # Refer test.yaml
14
+ module SqsPoller
15
+ module Process
16
+ class WorkerController
17
+
18
+ private_class_method :new
19
+
20
+ def initialize(worker_count, task_queue, worker_task, auto_tuning = true)
21
+ @task_queue = task_queue
22
+ @message_handler = worker_task
23
+ @dynamic_scheduling = auto_tuning
24
+ @worker_count = worker_count
25
+ @task_workers = Concurrent::RubyThreadPoolExecutor.new(min_threads: @worker_count)
26
+ @task_finalizer = TaskFinalizer.new(task_queue.max)
27
+ @logger = SqsPoller::Logger.get_new_logger(self.class.name)
28
+ @started = false
29
+ end
30
+
31
+ def self.get
32
+ return @instance if @instance
33
+ raise "WorkerController not yet started"
34
+ end
35
+
36
+ def self.start (worker_count, task_queue, worker_task, auto_tuning = true)
37
+ return @instance if @instance
38
+ @instance = new(worker_count, task_queue, worker_task, auto_tuning)
39
+ @instance.start
40
+ @instance
41
+ end
42
+
43
+ def started?
44
+ @started
45
+ end
46
+
47
+ def start
48
+ @logger.info "Starting #{@worker_count} workers"
49
+ begin
50
+ start_workers
51
+ @started = true
52
+ rescue Exception => e
53
+ @logger.error "Task Worker killed for and restarted. Caught error: #{e.message}, #{e.backtrace.join("\n")}"
54
+ end
55
+ end
56
+
57
+ def shutdown
58
+ @logger.info "WorkerController Terminated"
59
+ end
60
+
61
+ private
62
+
63
+ def start_workers
64
+ @worker_count.times do |index|
65
+ @task_workers.post do
66
+ @logger.info "Starting worker #{index}"
67
+ worker = Worker.new("Worker-#{index}", @task_queue, @task_finalizer, @message_handler)
68
+ loop do
69
+ begin
70
+ worker.run
71
+ rescue Exception => e
72
+ @logger.error "Task Worker killed for and restarted. Caught error: #{e.message}, #{e.backtrace.join("\n")}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,147 @@
1
+ require "logger"
2
+ require "concurrent"
3
+ require "net/http"
4
+ require "yaml"
5
+ require "erb"
6
+ require "sqspoller"
7
+ require "sqspoller/worker_task"
8
+ require "sqspoller/poll/queue_controller"
9
+ require "sqspoller/process/worker_controller"
10
+ require "sqspoller/process/task_finalizer"
11
+ require "sqspoller/process/message_handler"
12
+ require 'new_relic/agent'
13
+
14
+ # This is the starting point of SQS polling. Load the Yaml config and starts QueueController and WorkerController.
15
+ # Waits until process get kill or stop.
16
+ module SqsPoller
17
+ class << self
18
+
19
+ def sym(map)
20
+ if map.class == Hash
21
+ map = map.inject({}) { |memo, (k, v)| memo[k.to_sym] = sym(v); memo }
22
+ end
23
+ map
24
+ end
25
+
26
+ def daemonize(filename)
27
+ raise 'Must run as root' if Process.euid != 0
28
+
29
+ raise 'First fork failed' if (pid = fork) == -1
30
+ exit unless pid.nil?
31
+
32
+ Process.setsid
33
+ raise 'Second fork failed' if (pid = fork) == -1
34
+ exit unless pid.nil?
35
+ puts "Daemon pid: #{Process.pid}" # Or save it somewhere, etc.
36
+
37
+ Dir.chdir '/'
38
+ File.umask 0000
39
+
40
+ STDIN.reopen filename
41
+ STDOUT.reopen '/dev/null', 'a'
42
+ STDERR.reopen STDOUT
43
+ end
44
+
45
+ def start_poller_with_config(config, queue_config_name, access_key_id, secret_access_key, region, logger_file, log_level = ::Logger::ERROR)
46
+ SqsPoller::Logger.set_log_level(log_level)
47
+ SqsPoller::Logger.set_logger_file(logger_file)
48
+
49
+ ::NewRelic::Agent.manual_start if ENV['START_NEW_RELIC_AGENT']
50
+ SqsPoller::Metrics.start_metrics_agent if ENV['START_LOCAL_METRICS_AGENT']
51
+ @logger = SqsPoller::Logger.get_new_logger("SqsPoller")
52
+ @logger.info "Started poller method"
53
+ queues_config = config[queue_config_name] || config[queue_config_name.to_sym]
54
+ total_poller_threads = get_total_poller_threads(queues_config)
55
+ worker_configuration = config[:worker_configuration]
56
+ total_worker_threads = get_total_worker_threads(worker_configuration, total_poller_threads)
57
+ waiting_tasks_ratio = get_waiting_tasks_ratio(worker_configuration)
58
+ aws_config = {
59
+ :access_key_id => access_key_id,
60
+ :secret_access_key => secret_access_key,
61
+ :region => region
62
+ }
63
+
64
+ task_queue = SizedQueue.new(total_worker_threads * waiting_tasks_ratio)
65
+
66
+ qc = SqsPoller::Poller::QueueController.start queues_config, task_queue, aws_config
67
+ unless qc.started?
68
+ @logger.error("Unable to start Queue Pollers.")
69
+ return
70
+ end
71
+
72
+ worker_task = worker_configuration[:worker_class].split('::').inject(Object) { |o, c| o.const_get c }.new(worker_configuration)
73
+
74
+ wc = SqsPoller::Process::WorkerController.start total_worker_threads, task_queue, worker_task
75
+
76
+ unless wc.started?
77
+ @logger.error("Unable to start Workers.")
78
+ return
79
+ end
80
+
81
+ wait
82
+
83
+ end
84
+
85
+ def start_poller(filename, queue_config_name, access_key_id, secret_access_key, region, log_filename = nil)
86
+ begin
87
+ puts "Starting poller"
88
+ config = YAML.load(ERB.new(IO.read(filename)).result)
89
+ config = sym(config)
90
+
91
+ if log_filename.nil? || log_filename.empty?
92
+ puts "Did not receive log file name"
93
+ fork do
94
+ Process.daemon
95
+ start_poller_with_config config, queue_config_name, access_key_id, secret_access_key, region, STDOUT, ::Logger::DEBUG
96
+ end
97
+ else
98
+ puts "Did receive log file name #{log_filename}"
99
+ #daemonize log_filename
100
+ puts "Daemonize log file name #{log_filename}"
101
+ start_poller_with_config config, queue_config_name, access_key_id, secret_access_key, region, STDOUT, ::Logger::INFO
102
+ end
103
+ rescue Exception => e
104
+ puts "#{e}"
105
+ end
106
+ end
107
+
108
+ def get_total_poller_threads(queues_config)
109
+ total_poller_threads = 0
110
+ queues_config.keys.each { |queue|
111
+ total_poller_threads += queues_config[queue][:polling_threads]
112
+ }
113
+ total_poller_threads
114
+ end
115
+
116
+ def get_waiting_tasks_ratio(worker_configuration)
117
+ waiting_tasks_ratio = worker_configuration[:waiting_tasks_ratio]
118
+ waiting_tasks_ratio = 1 if waiting_tasks_ratio.nil?
119
+ waiting_tasks_ratio
120
+ end
121
+
122
+ def get_total_worker_threads(worker_configuration, total_poller_threads)
123
+ worker_thread_count = worker_configuration[:concurrency]
124
+ worker_thread_count = total_poller_threads if worker_thread_count.nil?
125
+ worker_thread_count
126
+ end
127
+
128
+ def timeout
129
+ n_bytes = [42].pack('i').size
130
+ n_bits = n_bytes * 8
131
+ 2 ** (n_bits - 2) - 1
132
+ end
133
+
134
+ private
135
+
136
+ def wait
137
+ t = Thread.new do
138
+ loop do
139
+ sleep(60 * 60)
140
+ end
141
+ end
142
+ t.join
143
+ end
144
+
145
+ end
146
+
147
+ end
@@ -0,0 +1,3 @@
1
+ module SqsPoller
2
+ VERSION = "2.0.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'sqspoller/process/message_handler'
2
+ # DEPRECATED: This class is to support backward compatibility. This class will be removed once we updated all the config files updated.
3
+ module Sqspoller
4
+
5
+ class WorkerTask
6
+
7
+ def initialize(worker_configuration)
8
+ @message_handler = SqsPoller::Process::MessageHandler.new(worker_configuration)
9
+ end
10
+
11
+ def handle(message, message_id)
12
+ @message_handler.handle(message, message_id)
13
+ end
14
+
15
+ end
16
+
17
+ end
data/lib/sqspoller.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "sqspoller/version"
2
+ require "seahorse/stringio"
3
+ require "sqspoller/sqs_poller"
4
+
5
+ module SqsPoller
6
+
7
+ end
data/lib/test.yaml ADDED
@@ -0,0 +1,15 @@
1
+ worker_configuration:
2
+ worker_class: "Sqspoller::WorkerTask"
3
+ http_method: "post"
4
+ http_url: "http://localhost:5001/process"
5
+ concurrency: 50 # => Total Number of service threads - threads occupied by cron jobs - thread for heartbeat => 2*32 - 8 - 1 = 55
6
+ waiting_tasks_ratio: 2
7
+ production:
8
+ my-queue:
9
+ polling_threads: 4
10
+ max_number_of_messages: 10
11
+ message_types: [ ]
12
+ my-queue1:
13
+ polling_threads: 2
14
+ max_number_of_messages: 10
15
+ message_types: [ ]
data/sqspoller.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sqspoller/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fourkites-sqspoller-v2"
8
+ spec.version = '1.0.0'
9
+ spec.authors = %w[anshul-fk nagarajan]
10
+ spec.email = %w[anshul@fourkites.com nagarajan.shanmugam@fourkites.com]
11
+
12
+ spec.summary = 'Gem to poll messages from SQS queue and process them'
13
+ spec.description = ''
14
+ spec.homepage = ''
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_runtime_dependency "concurrent-ruby"
25
+ spec.add_runtime_dependency "rest-client"
26
+ spec.add_runtime_dependency 'aws-sdk', '~> 3'
27
+ spec.add_runtime_dependency 'ruby-metrics'
28
+ spec.add_runtime_dependency 'newrelic_rpm'
29
+
30
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fourkites-sqspoller-v2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - anshul-fk
8
+ - nagarajan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2022-11-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.10'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.10'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: concurrent-ruby
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rest-client
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: aws-sdk
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3'
84
+ - !ruby/object:Gem::Dependency
85
+ name: ruby-metrics
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: newrelic_rpm
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ description: ''
113
+ email:
114
+ - anshul@fourkites.com
115
+ - nagarajan.shanmugam@fourkites.com
116
+ executables:
117
+ - run_sqs_poller
118
+ - sqs_poller.service
119
+ - sqs_poller_v2
120
+ extensions: []
121
+ extra_rdoc_files: []
122
+ files:
123
+ - ".gitignore"
124
+ - ".ruby-gemset"
125
+ - ".ruby-version"
126
+ - CODE_OF_CONDUCT.md
127
+ - Gemfile
128
+ - LICENSE
129
+ - README.md
130
+ - Rakefile
131
+ - bin/run_sqs_poller
132
+ - bin/sqs_poller.service
133
+ - bin/sqs_poller_v2
134
+ - images/SQS_Poller_V2.png
135
+ - install.sh
136
+ - lib/config/newrelic.yml
137
+ - lib/seahorse/stringio.rb
138
+ - lib/sqs-ruby-example-consume-queue.rb
139
+ - lib/sqs-ruby-example-create-queue.rb
140
+ - lib/sqspoller.rb
141
+ - lib/sqspoller/common/ring_buffer.rb
142
+ - lib/sqspoller/common/utils.rb
143
+ - lib/sqspoller/logger/logger.rb
144
+ - lib/sqspoller/metrics/log_reporter.rb
145
+ - lib/sqspoller/metrics/queue_stats_reporter.rb
146
+ - lib/sqspoller/metrics/sqs_poller_metrics.rb
147
+ - lib/sqspoller/poll/queue_controller.rb
148
+ - lib/sqspoller/poll/queue_poller.rb
149
+ - lib/sqspoller/process/message_handler.rb
150
+ - lib/sqspoller/process/task_finalizer.rb
151
+ - lib/sqspoller/process/task_worker.rb
152
+ - lib/sqspoller/process/worker_controller.rb
153
+ - lib/sqspoller/sqs_poller.rb
154
+ - lib/sqspoller/version.rb
155
+ - lib/sqspoller/worker_task.rb
156
+ - lib/test.yaml
157
+ - sqspoller.gemspec
158
+ homepage: ''
159
+ licenses: []
160
+ metadata: {}
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubygems_version: 3.3.14
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: Gem to poll messages from SQS queue and process them
180
+ test_files: []