rabbitek 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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