pika_que 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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +41 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/examples/demo.rb +42 -0
  13. data/examples/demo_delay.rb +41 -0
  14. data/examples/demo_oneoff.rb +29 -0
  15. data/examples/demo_priority.rb +52 -0
  16. data/examples/demo_reporter.rb +19 -0
  17. data/examples/demo_retry.rb +41 -0
  18. data/examples/demo_worker.rb +17 -0
  19. data/examples/dev_worker.rb +19 -0
  20. data/exe/pika_que +8 -0
  21. data/lib/active_job/queue_adapters/pika_que_adapter.rb +42 -0
  22. data/lib/pika_que.rb +44 -0
  23. data/lib/pika_que/broker.rb +88 -0
  24. data/lib/pika_que/cli.rb +180 -0
  25. data/lib/pika_que/codecs/json.rb +22 -0
  26. data/lib/pika_que/codecs/noop.rb +20 -0
  27. data/lib/pika_que/codecs/rails.rb +22 -0
  28. data/lib/pika_que/configuration.rb +110 -0
  29. data/lib/pika_que/connection.rb +47 -0
  30. data/lib/pika_que/delay_worker.rb +55 -0
  31. data/lib/pika_que/errors.rb +5 -0
  32. data/lib/pika_que/handlers/default_handler.rb +31 -0
  33. data/lib/pika_que/handlers/delay_handler.rb +124 -0
  34. data/lib/pika_que/handlers/error_handler.rb +69 -0
  35. data/lib/pika_que/handlers/retry_handler.rb +186 -0
  36. data/lib/pika_que/launcher.rb +92 -0
  37. data/lib/pika_que/logging.rb +33 -0
  38. data/lib/pika_que/metrics.rb +26 -0
  39. data/lib/pika_que/metrics/log_metric.rb +23 -0
  40. data/lib/pika_que/metrics/null_metric.rb +14 -0
  41. data/lib/pika_que/middleware/active_record.rb +13 -0
  42. data/lib/pika_que/middleware/chain.rb +90 -0
  43. data/lib/pika_que/processor.rb +45 -0
  44. data/lib/pika_que/publisher.rb +23 -0
  45. data/lib/pika_que/rails.rb +62 -0
  46. data/lib/pika_que/reporters.rb +18 -0
  47. data/lib/pika_que/reporters/log_reporter.rb +13 -0
  48. data/lib/pika_que/runner.rb +24 -0
  49. data/lib/pika_que/subscriber.rb +80 -0
  50. data/lib/pika_que/util.rb +17 -0
  51. data/lib/pika_que/version.rb +3 -0
  52. data/lib/pika_que/worker.rb +99 -0
  53. data/pika_que.gemspec +37 -0
  54. metadata +181 -0
@@ -0,0 +1,13 @@
1
+ module PikaQue
2
+ module Reporters
3
+ class LogReporter
4
+
5
+ def report(ex, clazz, msg)
6
+ PikaQue.logger.debug "error processing <#{msg}>"
7
+ PikaQue.logger.error "Exception #{ex.class} in #{clazz}: #{ex.message}" unless ex.nil?
8
+ PikaQue.logger.error ex.backtrace.join("\n") unless ex.nil? || ex.backtrace.nil?
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module PikaQue
2
+ class Runner
3
+
4
+ def run
5
+ run_config = {}
6
+
7
+ # TODO anything to add to run_config?
8
+
9
+ @processors = []
10
+ PikaQue.config[:processors].each do |processor_hash|
11
+ _processor = PikaQue::Util.constantize(processor_hash[:processor]).new(processor_hash.merge(run_config))
12
+ _processor.start
13
+ @processors << _processor
14
+ end
15
+ end
16
+
17
+ # halt? pause?
18
+ def stop
19
+ @processors.each(&:stop)
20
+ PikaQue.connection.disconnect!
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ require 'pika_que/reporters'
2
+ require 'pika_que/metrics'
3
+
4
+ module PikaQue
5
+ class Subscriber
6
+ include Logging
7
+ include Reporters
8
+ include Metrics
9
+
10
+ attr_accessor :broker, :pool, :queue, :handler
11
+
12
+ def initialize(opts = {})
13
+ @opts = PikaQue.config.merge(opts)
14
+ @codec = @opts[:codec]
15
+ @broker = @opts[:broker] || PikaQue::Broker.new(nil, @opts).tap{ |b| b.start }
16
+ @pool = @opts[:worker_pool] || Concurrent::FixedThreadPool.new(@opts[:concurrency] || 1)
17
+ end
18
+
19
+ def setup_queue(queue_name, queue_opts)
20
+ @queue = broker.queue(queue_name, @opts[:queue_options].merge(queue_opts))
21
+ end
22
+
23
+ def setup_handler(handler_class, handler_opts)
24
+ @handler = broker.handler(handler_class, handler_opts)
25
+ # TODO use routing keys?
26
+ @handler.bind_queue(@queue, @queue.name)
27
+ end
28
+
29
+ def subscribe(worker)
30
+ @consumer = queue.subscribe(:block => false, :manual_ack => @opts[:ack], :arguments => worker.consumer_arguments) do | delivery_info, metadata, msg |
31
+ # TODO make idletime configurable on thread pool? default is 60.
32
+ pool.post do
33
+ res = nil
34
+ error = nil
35
+ begin
36
+ decoded_msg = @codec.decode(msg)
37
+ metrics.measure("work.#{self.class.name}.time") do
38
+ PikaQue.middleware.invoke(self, delivery_info, metadata, decoded_msg) do
39
+ res = worker.work(delivery_info, metadata, decoded_msg)
40
+ end
41
+ end
42
+ logger.debug "done processing #{res} <#{msg}>"
43
+ rescue => worker_err
44
+ res = :error
45
+ error = worker_err
46
+ notify_reporters(worker_err, worker.class, msg)
47
+ end
48
+
49
+ if @opts[:ack]
50
+ begin
51
+ handler.handle(res, broker.channel, delivery_info, metadata, msg, error)
52
+ metrics.increment("work.#{self.class.name}.handled.#{res}")
53
+ rescue => handler_err
54
+ notify_reporters(handler_err, handler.class, msg)
55
+ metrics.increment("work.#{self.class.name}.handler.error")
56
+ end
57
+ else
58
+ metrics.increment("work.#{self.class.name}.handled.noop")
59
+ end
60
+ metrics.increment("work.#{self.class.name}.processed")
61
+ end
62
+ end
63
+ end
64
+
65
+ def unsubscribe
66
+ @consumer.cancel if @consumer
67
+ @consumer = nil
68
+ end
69
+
70
+ def teardown
71
+ unless @opts[:worker_pool]
72
+ @pool.shutdown
73
+ @pool.wait_for_termination 12
74
+ end
75
+ broker.cleanup
76
+ broker.stop
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,17 @@
1
+ module PikaQue
2
+ module Util
3
+ extend self
4
+
5
+ def constantize(str)
6
+ return str if str.is_a? Class
7
+
8
+ names = str.split('::')
9
+ names.shift if names.empty? || names.first.empty?
10
+
11
+ names.inject(Object) do |constant, name|
12
+ constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module PikaQue
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,99 @@
1
+ require 'pika_que/subscriber'
2
+
3
+ module PikaQue
4
+ module Worker
5
+
6
+ def initialize(opts = {})
7
+ @subscriber = PikaQue::Subscriber.new(opts)
8
+ end
9
+
10
+ def prepare
11
+ @subscriber.setup_queue(self.class.queue_name, self.class.queue_opts)
12
+ @subscriber.setup_handler(self.class.handler_class, self.class.handler_opts)
13
+ end
14
+
15
+ def run
16
+ @subscriber.subscribe(self)
17
+ end
18
+
19
+ def start
20
+ prepare
21
+ run
22
+ end
23
+
24
+ def stop
25
+ @subscriber.unsubscribe
26
+ @subscriber.teardown
27
+ end
28
+
29
+ def work(delivery_info, metadata, msg)
30
+ perform(msg)
31
+ end
32
+
33
+ def ack!; :ack end
34
+ def reject!; :reject; end
35
+ def requeue!; :requeue; end
36
+
37
+ def logger
38
+ PikaQue.logger
39
+ end
40
+
41
+ def consumer_arguments
42
+ self.class.priority.nil? ? {} : { :'x-priority' => self.class.priority }
43
+ end
44
+
45
+ def self.included(base)
46
+ base.extend ClassMethods
47
+ end
48
+
49
+ module ClassMethods
50
+ attr_reader :queue_name
51
+ attr_reader :queue_opts
52
+ attr_reader :handler_class
53
+ attr_reader :handler_opts
54
+ attr_reader :priority
55
+ attr_reader :config
56
+
57
+ def from_queue(q, opts={})
58
+ @queue_name = q.to_s
59
+ @priority = opts.delete(:priority)
60
+ @queue_opts = PikaQue.config[:queue_options].merge(opts)
61
+ end
62
+
63
+ def handle_with(handler, opts={})
64
+ @handler_class = handler
65
+ @handler_opts = PikaQue.config[:handler_options].merge(opts)
66
+ end
67
+
68
+ def enqueue(msg, opts={})
69
+ opts[:routing_key] ||= (queue_opts[:routing_key] if queue_opts)
70
+ opts[:to_queue] ||= queue_name
71
+ opts[:priority] ||= priority
72
+
73
+ publisher.publish(msg, opts)
74
+ end
75
+ alias_method :perform_async, :enqueue
76
+
77
+ def enqueue_at(msg, timestamp, opts={})
78
+ opts[:to_queue] ||= "#{PikaQue.config[:exchange]}-delay"
79
+ work_queue = opts.delete(:routing_key) || (queue_opts[:routing_key] if queue_opts) || queue_name
80
+ opts[:headers] = { work_at: timestamp, work_queue: work_queue }
81
+
82
+ publisher.publish(msg, opts)
83
+ end
84
+ alias_method :perform_at, :enqueue
85
+
86
+ def config(opts={})
87
+ @config = opts
88
+ end
89
+
90
+ private
91
+
92
+ def publisher
93
+ @publisher ||= PikaQue::Publisher.new(config || {})
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pika_que/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pika_que"
8
+ spec.version = PikaQue::VERSION
9
+ spec.authors = ["Dong Wook Koo"]
10
+ spec.email = ["dwkoogt@gmail.com"]
11
+
12
+ spec.summary = %q{Ruby background processor for RabbitMQ.}
13
+ spec.description = %q{Ruby background processor for RabbitMQ.}
14
+ spec.homepage = "https://github.com/dwkoogt/pika_que"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency 'bunny', '~> 2.6'
31
+ spec.add_dependency 'concurrent-ruby', '~> 1.0'
32
+ spec.add_dependency 'json', '~> 1.8'
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.11"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ end
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pika_que
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dong Wook Koo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bunny
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: concurrent-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: Ruby background processor for RabbitMQ.
98
+ email:
99
+ - dwkoogt@gmail.com
100
+ executables:
101
+ - pika_que
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - bin/console
114
+ - bin/setup
115
+ - examples/demo.rb
116
+ - examples/demo_delay.rb
117
+ - examples/demo_oneoff.rb
118
+ - examples/demo_priority.rb
119
+ - examples/demo_reporter.rb
120
+ - examples/demo_retry.rb
121
+ - examples/demo_worker.rb
122
+ - examples/dev_worker.rb
123
+ - exe/pika_que
124
+ - lib/active_job/queue_adapters/pika_que_adapter.rb
125
+ - lib/pika_que.rb
126
+ - lib/pika_que/broker.rb
127
+ - lib/pika_que/cli.rb
128
+ - lib/pika_que/codecs/json.rb
129
+ - lib/pika_que/codecs/noop.rb
130
+ - lib/pika_que/codecs/rails.rb
131
+ - lib/pika_que/configuration.rb
132
+ - lib/pika_que/connection.rb
133
+ - lib/pika_que/delay_worker.rb
134
+ - lib/pika_que/errors.rb
135
+ - lib/pika_que/handlers/default_handler.rb
136
+ - lib/pika_que/handlers/delay_handler.rb
137
+ - lib/pika_que/handlers/error_handler.rb
138
+ - lib/pika_que/handlers/retry_handler.rb
139
+ - lib/pika_que/launcher.rb
140
+ - lib/pika_que/logging.rb
141
+ - lib/pika_que/metrics.rb
142
+ - lib/pika_que/metrics/log_metric.rb
143
+ - lib/pika_que/metrics/null_metric.rb
144
+ - lib/pika_que/middleware/active_record.rb
145
+ - lib/pika_que/middleware/chain.rb
146
+ - lib/pika_que/processor.rb
147
+ - lib/pika_que/publisher.rb
148
+ - lib/pika_que/rails.rb
149
+ - lib/pika_que/reporters.rb
150
+ - lib/pika_que/reporters/log_reporter.rb
151
+ - lib/pika_que/runner.rb
152
+ - lib/pika_que/subscriber.rb
153
+ - lib/pika_que/util.rb
154
+ - lib/pika_que/version.rb
155
+ - lib/pika_que/worker.rb
156
+ - pika_que.gemspec
157
+ homepage: https://github.com/dwkoogt/pika_que
158
+ licenses:
159
+ - MIT
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
+ rubyforge_project:
177
+ rubygems_version: 2.4.5.2
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: Ruby background processor for RabbitMQ.
181
+ test_files: []