rabbitek 0.3.2 → 0.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78ba89a7e1863c0c5db9b8abb89b59d145075c3250fbf4bb679e854e8f62a181
4
- data.tar.gz: c3ecf9feae5fd64141d354a622db4946286297113db75c3fbfa5eef242bd3116
3
+ metadata.gz: 19938345d3105a6f1fc72afaab8a04c1df84d6a528b8af255c9173facc119329
4
+ data.tar.gz: 579f14f003bc200d073d0a2ceec1f6c44483939e3ec82905cfb98f4cd49c7c16
5
5
  SHA512:
6
- metadata.gz: '0311083dcdd6d29b9f6635fd3d8c11ab09b0a6237c76400a12c4856794412069901be0e80f058de46b094b50b0fccd3c77f67d570d91a27c632fca07335788df'
7
- data.tar.gz: 411bdd19f9822adbca577ce86d13e47fbc7b0f0b615de0cd8aa0a42a46b2d6aeb7e88ba7955614749aa8d686975b5bf034d83103760455e6a5c35744a905561c
6
+ metadata.gz: 18a5fd3d4bf1f947b0fe8935c48a7dfa37d831fa4eea9396caa935ebb63ec9b04c05d518a28ebf68f11e3b34f76cb4b55a0c58886b465d12ab2c911ce5f9b45b
7
+ data.tar.gz: 6e8ffe529da3cb2a1fdccd75f5d2de6ef0b3bee54de8b38854f09e659f5c91a160bfce09f89240b7d46e3c997e6c35446c9f5129481e755af08c5c7126273801
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## v0.4.0
2
+
3
+ Improvements:
4
+ * Add metrics (universal, you can use e.g. prometheus)
5
+
6
+ ## v0.3.5
7
+
8
+ Bugfix:
9
+ * Fix OpenTracing support when there is no trace_id incoming with job.
10
+
11
+ ## v0.3.4
12
+
13
+ Improvements:
14
+ * Logging improvement
15
+
16
+ ## v0.3.3
17
+
18
+ Improvements:
19
+ * Failure in retry hook will result in additional logging and nacking job with requeue: true
20
+
1
21
  ## v0.3.2
2
22
 
3
23
  ## v0.3.1
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rabbitek (0.3.2)
4
+ rabbitek (0.4.0)
5
5
  activesupport (> 3.0)
6
6
  bunny (~> 2.11.0)
7
7
  oj (~> 3.6)
8
8
  opentracing (~> 0.4)
9
9
  slop (~> 4.0)
10
+ yabeda
10
11
 
11
12
  GEM
12
13
  remote: https://rubygems.org/
@@ -62,6 +63,7 @@ GEM
62
63
  concurrent-ruby (1.1.5)
63
64
  crass (1.0.6)
64
65
  diff-lcs (1.3)
66
+ dry-initializer (3.0.3)
65
67
  erubi (1.9.0)
66
68
  globalid (0.4.2)
67
69
  activesupport (>= 4.2.0)
@@ -159,6 +161,9 @@ GEM
159
161
  websocket-driver (0.7.1)
160
162
  websocket-extensions (>= 0.1.0)
161
163
  websocket-extensions (0.1.4)
164
+ yabeda (0.3.0)
165
+ concurrent-ruby
166
+ dry-initializer
162
167
 
163
168
  PLATFORMS
164
169
  ruby
data/README.md CHANGED
@@ -11,6 +11,7 @@ High performance, easy to use background job processing library for Ruby using R
11
11
  * OpenTracing (http://opentracing.io/) instrumentation
12
12
  * NewRelic instrumentation for sending errors
13
13
  * Sentry instrumentation for sending errors
14
+ * Metrics (using [Yabeda](https://github.com/yabeda-rb/yabeda))
14
15
 
15
16
  ## Installation
16
17
 
@@ -89,7 +90,6 @@ You can schedule jobs e.g.: `ExampleCustomer.perform_async(some: :payload)`
89
90
  * dead queue
90
91
  * CRON jobs
91
92
  * extended docs and how to
92
- * prometheus metrics
93
93
 
94
94
 
95
95
  ## Development
data/lib/rabbitek.rb CHANGED
@@ -6,6 +6,7 @@ require 'bunny'
6
6
  require 'oj'
7
7
  require 'opentracing'
8
8
  require 'logger'
9
+ require 'yabeda'
9
10
 
10
11
  # active_support
11
12
  require 'active_support/core_ext/module/attribute_accessors'
@@ -54,3 +55,15 @@ module Rabbitek
54
55
  @bunny_connection ||= BunnyConnection.initialize_connection
55
56
  end
56
57
  end
58
+
59
+ Yabeda.configure do
60
+ group :rabbitek do
61
+ counter :processed_messages_count, comment: 'Total number of all messages'
62
+ counter :errored_messages_count, comment: 'Total number of errored messages'
63
+ histogram :processed_messages_runtime do
64
+ comment 'How long it takes to process message'
65
+ unit :seconds
66
+ buckets [0.1, 0.5, 1, 5, 10, 30, 60]
67
+ end
68
+ end
69
+ end
data/lib/rabbitek/cli.rb CHANGED
@@ -77,7 +77,8 @@ module Rabbitek
77
77
  end
78
78
 
79
79
  def boot_consumers
80
- (1..configuration[:threads]).each_with_object([]) do |_, arr|
80
+ (1..configuration[:threads]).each_with_object([]) do |i, arr|
81
+ debug "Booting thread ##{i}"
81
82
  arr << Starter.new(Rabbitek.bunny_connection, configuration).start
82
83
  end
83
84
  end
@@ -21,6 +21,10 @@ module Rabbitek
21
21
  channel.ack(delivery_info.delivery_tag, multiple)
22
22
  end
23
23
 
24
+ def nack!(delivery_info, multiple = false, requeue = true)
25
+ channel.nack(delivery_info.delivery_tag, multiple, requeue)
26
+ end
27
+
24
28
  def logger
25
29
  Rabbitek.logger
26
30
  end
@@ -8,12 +8,29 @@ module Rabbitek
8
8
  ##
9
9
  # Hook to retry failed jobs
10
10
  class Retry < Rabbitek::ServerHook
11
+ include Loggable
12
+
11
13
  def call(consumer, message)
12
14
  super
13
15
  rescue StandardError
14
- Retryer.call(consumer, message) unless consumer.batch_size
16
+ retry_message(consumer, message) unless consumer.batch_size
15
17
  raise
16
18
  end
19
+
20
+ private
21
+
22
+ def retry_message(consumer, message)
23
+ Retryer.call(consumer, message)
24
+ rescue StandardError => e
25
+ error(
26
+ message: 'Critical error while retrying. Nacking and requeueing message',
27
+ consumer: message.delivery_info.routing_key,
28
+ jid: consumer.jid,
29
+ error: e.to_s
30
+ )
31
+
32
+ consumer.nack!(message.delivery_info)
33
+ end
17
34
  end
18
35
  end
19
36
  end
@@ -11,19 +11,40 @@ module Rabbitek
11
11
  include Loggable
12
12
 
13
13
  def call(consumer, message)
14
- info(message: 'Starting', consumer: message.delivery_info.routing_key, jid: consumer.jid)
14
+ log_started(consumer, message)
15
15
 
16
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
+ start = current_time
17
17
 
18
18
  super
19
19
  ensure
20
+ total_time = current_time - start
21
+
22
+ log_finished(consumer, message, total_time)
23
+ metrics_measure_time(consumer, total_time)
24
+ end
25
+
26
+ private
27
+
28
+ def current_time
29
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
30
+ end
31
+
32
+ def log_started(consumer, message)
33
+ info(message: 'Starting job', consumer: message.delivery_info.routing_key, jid: consumer.jid)
34
+ end
35
+
36
+ def log_finished(consumer, message, total_time)
20
37
  info(
21
- message: 'Finished',
38
+ message: 'Finished job',
22
39
  consumer: message.delivery_info.routing_key,
23
- time: Process.clock_gettime(Process::CLOCK_MONOTONIC) - start,
40
+ time: total_time,
24
41
  jid: consumer.jid
25
42
  )
26
43
  end
44
+
45
+ def metrics_measure_time(consumer, total_time)
46
+ Yabeda.rabbitek.processed_messages_runtime.measure({ consumer: consumer.class }, total_time)
47
+ end
27
48
  end
28
49
  end
29
50
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Rabbitek
6
+ ##
7
+ # Single message processor
8
+ class MessageProcessor
9
+ include Loggable
10
+ extend Forwardable
11
+
12
+ def initialize(starter, delivery_info, properties, payload)
13
+ @starter = starter
14
+ @delivery_info = delivery_info
15
+ @properties = properties
16
+ @payload = payload
17
+ end
18
+
19
+ def process
20
+ consumer.set_context
21
+
22
+ metrics_add_processed_count
23
+
24
+ hook_walker = Utils::HookWalker.new(Rabbitek.config.server_hooks)
25
+ hook_walker.call!(consumer, message) { |*args| run_job(*args) }
26
+ rescue StandardError => e
27
+ on_message_errored(e)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :starter, :delivery_info, :properties, :payload
33
+ def_delegators :starter, :channel, :work_queue, :retry_or_delayed_queue, :retry_or_delayed_exchange
34
+
35
+ def run_job(modified_consumer, message)
36
+ if modified_consumer.class.batch
37
+ run_job_batched(modified_consumer, message)
38
+ else
39
+ modified_consumer.perform(message)
40
+ modified_consumer.ack!(message.delivery_info)
41
+ end
42
+ end
43
+
44
+ def run_job_batched(modified_consumer, message)
45
+ Batcher.new(modified_consumer).perform(message) do |batch|
46
+ modified_consumer.perform(batch)
47
+ modified_consumer.ack!(batch.last.delivery_info, true)
48
+ end
49
+ end
50
+
51
+ def on_message_errored(exception)
52
+ error(message: exception.inspect, backtrace: exception.backtrace, consumer: consumer.class, jid: consumer.jid)
53
+ metrics_add_errored_count
54
+ end
55
+
56
+ def metrics_add_processed_count
57
+ Yabeda.rabbitek.processed_messages_count.increment(metrics_tags, by: 1)
58
+ end
59
+
60
+ def metrics_add_errored_count
61
+ Yabeda.rabbitek.errored_messages_count.increment(metrics_tags, by: 1)
62
+ end
63
+
64
+ def message
65
+ @message ||= Message.new(delivery_info: delivery_info, properties: properties, payload: payload)
66
+ end
67
+
68
+ def consumer
69
+ @consumer ||= consumer_instance(message.delivery_info.routing_key)
70
+ end
71
+
72
+ def consumer_instance(routing_key)
73
+ Thread.current[:worker_classes] ||= {}
74
+ klass = Thread.current[:worker_classes][routing_key] ||= routing_key.constantize
75
+ klass.new(channel, work_queue, retry_or_delayed_queue, retry_or_delayed_exchange)
76
+ rescue NameError
77
+ nil # TODO: to dead queue
78
+ end
79
+
80
+ def metrics_tags
81
+ { consumer: consumer.class }
82
+ end
83
+ end
84
+ end
@@ -17,58 +17,18 @@ module Rabbitek
17
17
  setup_bindings!
18
18
 
19
19
  work_queue.subscribe(manual_ack: true) do |delivery_info, properties, payload|
20
- message = Message.new(delivery_info: delivery_info, properties: properties, payload: payload)
21
- Rabbitek.reloader.call { on_message_received(message) }
20
+ Rabbitek.reloader.call do
21
+ MessageProcessor.new(self, delivery_info, properties, payload).process
22
+ end
22
23
  end
23
24
  end
24
25
 
25
- private
26
-
27
- attr_reader :connection, :queue_name, :consumers, :opts
28
-
29
- def setup_bindings!
30
- consumers.each do |worker_class|
31
- work_queue.bind(work_exchange, routing_key: worker_class.to_s)
32
- retry_or_delayed_queue.bind(retry_or_delayed_exchange, routing_key: worker_class.to_s)
33
- end
34
- end
35
-
36
- def on_message_received(message)
37
- consumer = consumer_instance(message.delivery_info.routing_key)
38
- consumer.set_context
39
-
40
- hook_walker = Utils::HookWalker.new(Rabbitek.config.server_hooks)
41
-
42
- hook_walker.call!(consumer, message) do |*args|
43
- run_job(*args)
44
- end
45
- rescue StandardError => e
46
- error(message: e.inspect, backtrace: e.backtrace, consumer: consumer.class, jid: consumer.jid)
47
- end
48
-
49
- def run_job(consumer, message)
50
- if consumer.class.batch
51
- run_job_batched(consumer, message)
52
- else
53
- consumer.perform(message)
54
- consumer.ack!(message.delivery_info)
55
- end
56
- end
57
-
58
- def consumer_instance(routing_key)
59
- Thread.current[:worker_classes] ||= {}
60
- klass = Thread.current[:worker_classes][routing_key] ||= routing_key.constantize
61
- klass.new(channel, work_queue, retry_or_delayed_queue, retry_or_delayed_exchange)
62
- rescue NameError
63
- nil # TODO: to dead queue
64
- end
65
-
66
26
  def channel
67
27
  @channel ||= begin
68
- channel = connection.create_channel
69
- channel.basic_qos(opts[:basic_qos]) if opts[:basic_qos].present?
70
- channel
71
- end
28
+ channel = connection.create_channel
29
+ channel.basic_qos(opts[:basic_qos]) if opts[:basic_qos].present?
30
+ channel
31
+ end
72
32
  end
73
33
 
74
34
  def work_exchange
@@ -95,10 +55,14 @@ module Rabbitek
95
55
  )
96
56
  end
97
57
 
98
- def run_job_batched(consumer, message)
99
- Batcher.new(consumer).perform(message) do |batch|
100
- consumer.perform(batch)
101
- consumer.ack!(batch.last.delivery_info, true)
58
+ private
59
+
60
+ attr_reader :connection, :queue_name, :consumers, :opts
61
+
62
+ def setup_bindings!
63
+ consumers.each do |worker_class|
64
+ work_queue.bind(work_exchange, routing_key: worker_class.to_s)
65
+ retry_or_delayed_queue.bind(retry_or_delayed_exchange, routing_key: worker_class.to_s)
102
66
  end
103
67
  end
104
68
  end
@@ -55,7 +55,10 @@ module Rabbitek
55
55
  private
56
56
 
57
57
  def server_references(message_properties)
58
- ::OpenTracing::Reference.follows_from(extract(message_properties))
58
+ ctx = extract(message_properties)
59
+ return unless ctx
60
+
61
+ ::OpenTracing::Reference.follows_from(ctx)
59
62
  end
60
63
 
61
64
  def extract(message_properties)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabbitek
4
- VERSION = '0.3.2'
4
+ VERSION = '0.4.1'
5
5
  end
data/rabbitek.gemspec CHANGED
@@ -31,9 +31,10 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_dependency 'activesupport', '> 3.0'
33
33
  spec.add_dependency 'bunny', '~> 2.11.0'
34
- spec.add_dependency 'oj', '~> 3.6'
34
+ spec.add_dependency 'oj', '> 2.0.0'
35
35
  spec.add_dependency 'opentracing', '~> 0.4'
36
36
  spec.add_dependency 'slop', '~> 4.0'
37
+ spec.add_dependency 'yabeda'
37
38
 
38
39
  spec.add_development_dependency 'bundler', '~> 2.0'
39
40
  spec.add_development_dependency 'pry'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rabbitek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boostcom
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-14 00:00:00.000000000 Z
11
+ date: 2020-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: oj
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.6'
47
+ version: 2.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.6'
54
+ version: 2.0.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: opentracing
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '4.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yabeda
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -204,6 +218,7 @@ files:
204
218
  - lib/rabbitek/server/hooks/retry.rb
205
219
  - lib/rabbitek/server/hooks/time_tracker.rb
206
220
  - lib/rabbitek/server/message.rb
221
+ - lib/rabbitek/server/message_processor.rb
207
222
  - lib/rabbitek/server/retryer.rb
208
223
  - lib/rabbitek/server/server_hook.rb
209
224
  - lib/rabbitek/server/starter.rb
@@ -236,7 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
236
251
  - !ruby/object:Gem::Version
237
252
  version: '0'
238
253
  requirements: []
239
- rubygems_version: 3.0.3
254
+ rubyforge_project:
255
+ rubygems_version: 2.7.6
240
256
  signing_key:
241
257
  specification_version: 4
242
258
  summary: High performance background job processing