yabeda-delayed_job 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.
@@ -0,0 +1,9 @@
1
+ [paths]
2
+ provisioning = /etc/grafana/provisioning
3
+
4
+ [server]
5
+ http_port = 9091
6
+
7
+ [users]
8
+ default_theme = dark
9
+
@@ -0,0 +1,8 @@
1
+ apiVersion: 1
2
+ datasources:
3
+ - name: Prometheus
4
+ type: prometheus
5
+ access: proxy
6
+ url: http://prometheus:9090
7
+ isDefault: true
8
+
data/example/jobs.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RandomJob
4
+ def call
5
+ sleep(rand(5000) / 1000.0)
6
+ end
7
+ end
8
+
9
+ class RandomFailJob
10
+ def call
11
+ raise StandardError if (rand(100) % 3).zero?
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # Logfile created on 2021-06-21 18:52:52 +0000 by logger.rb/v1.4.3
2
+ I, [2021-06-21T18:52:52.998760 #85] INFO -- : 2021-06-21T18:52:52+0000: [Worker(delayed_job host:d7008330188a pid:85)] Starting job worker
3
+ I, [2021-06-21T18:52:53.031345 #85] INFO -- : 2021-06-21T18:52:53+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=1) RUNNING
4
+ I, [2021-06-21T18:52:55.246040 #85] INFO -- : 2021-06-21T18:52:55+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=1) COMPLETED after 2.2140
5
+ I, [2021-06-21T18:52:55.249806 #85] INFO -- : 2021-06-21T18:52:55+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=2) RUNNING
6
+ I, [2021-06-21T18:52:59.055058 #85] INFO -- : 2021-06-21T18:52:59+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=2) COMPLETED after 3.8045
7
+ I, [2021-06-21T18:52:59.058246 #85] INFO -- : 2021-06-21T18:52:59+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=3) RUNNING
8
+ I, [2021-06-21T18:52:59.909979 #85] INFO -- : 2021-06-21T18:52:59+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=3) COMPLETED after 0.8510
9
+ I, [2021-06-21T18:52:59.914425 #85] INFO -- : 2021-06-21T18:52:59+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=4) RUNNING
10
+ I, [2021-06-21T18:53:00.916535 #85] INFO -- : 2021-06-21T18:53:00+0000: [Worker(delayed_job host:d7008330188a pid:85)] Exiting...
11
+ I, [2021-06-21T18:53:04.921236 #85] INFO -- : 2021-06-21T18:53:04+0000: [Worker(delayed_job host:d7008330188a pid:85)] Job RandomJob#call (id=4) COMPLETED after 5.0059
12
+ I, [2021-06-21T18:53:04.922786 #85] INFO -- : 2021-06-21T18:53:04+0000: [Worker(delayed_job host:d7008330188a pid:85)] 4 jobs processed at 0.3355 j/s, 0 failed
@@ -0,0 +1,19 @@
1
+ global:
2
+ scrape_interval: 5s
3
+ scrape_timeout: 5s
4
+ evaluation_interval: 5s
5
+
6
+ scrape_configs:
7
+ - job_name: 'enqueuer'
8
+ scheme: http
9
+ metrics_path: '/metrics'
10
+ static_configs:
11
+ - targets:
12
+ - 'enqueuer:9394'
13
+ - job_name: 'delayed_job'
14
+ scheme: http
15
+ metrics_path: '/metrics'
16
+ static_configs:
17
+ - targets:
18
+ - 'delayed_job:9394'
19
+
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ FileUtils.rm_rf('/tmp/prometheus')
5
+
6
+ require 'yabeda/prometheus'
7
+ data_store = Prometheus::Client::DataStores::DirectFileStore.new(dir: '/tmp/prometheus')
8
+ Prometheus::Client.config.data_store = data_store
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yabeda/delayed_job/version'
4
+ require 'delayed_job'
5
+ require 'yabeda'
6
+ require 'yabeda/delayed_job/plugin'
7
+
8
+ module Yabeda
9
+ module DelayedJob
10
+ class Error < StandardError; end
11
+
12
+ LONG_RUNNING_JOB_RUNTIME_BUCKETS = [
13
+ 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, # standard (from Prometheus)
14
+ 30, 60, 120, 300, 1800, 3600, 21_600 # DelayedJob tasks may be very long-running
15
+ ].freeze
16
+
17
+ ::Yabeda.configure do
18
+ group :delayed_job
19
+
20
+ counter :jobs_enqueued_total, tags: %i[queue worker], comment: 'A counter of the total number of jobs enqueued.'
21
+ counter :jobs_failed_total, tags: %i[queue worker error], comment: 'asdf'
22
+ counter :jobs_errored_total, tags: %i[queue worker error], comment: 'asdf'
23
+
24
+ histogram :job_runtime, comment: 'A histogram of the job execution time.',
25
+ unit: :seconds, per: :job,
26
+ tags: %i[queue worker],
27
+ buckets: LONG_RUNNING_JOB_RUNTIME_BUCKETS
28
+ gauge :running_job_runtime, tags: %i[queue worker], aggregation: :max, unit: :seconds,
29
+ comment: 'How long currently running jobs are running ' \
30
+ '(useful for detection of hung jobs)'
31
+ gauge :jobs_waiting_count, tags: %i[queue], comment: 'The number of jobs waiting to process in sidekiq.'
32
+ gauge :queue_latency, tags: %i[queue],
33
+ comment: 'The queue latency, the difference in seconds since the oldest ' \
34
+ 'job in the queue was enqueued'
35
+
36
+ collect do
37
+ if ::Yabeda::DelayedJob.server?
38
+ Yabeda::DelayedJob.track_max_job_runtime
39
+ ::Yabeda::DelayedJob.track_database_metrics if ::Yabeda::DelayedJob.active_record_adapter?
40
+ end
41
+ end
42
+ end
43
+
44
+ class << self
45
+ def server?
46
+ require 'delayed/command'
47
+ @server ||= ObjectSpace.each_object(Delayed::Command).any?
48
+ end
49
+
50
+ def labelize(job)
51
+ result = { queue: job.queue, worker: job.name }
52
+ result.merge!(error: job.error.class.name) if job.error
53
+ result
54
+ end
55
+
56
+ def track_database_metrics
57
+ job_scope.select(:queue).count.each do |queue, count|
58
+ Yabeda.delayed_job.jobs_waiting_count.set({ queue: queue }, count)
59
+ end
60
+ job_scope.select('queue, max(NOW() - run_at) latency').each do |job|
61
+ Yabeda.delayed_job.queue_latency.set({ queue: job.queue }, job.latency)
62
+ end
63
+ end
64
+
65
+ def job_scope
66
+ db_time_now = ::Delayed::Worker.backend.db_time_now
67
+ ::Delayed::Worker.backend.where(
68
+ '(run_at <= ? AND (locked_at IS NULL OR locked_at < ?)) AND failed_at IS NULL',
69
+ db_time_now,
70
+ db_time_now - ::Delayed::Worker.max_run_time
71
+ ).group(:queue)
72
+ end
73
+
74
+ def active_record_adapter?
75
+ defined?(Delayed::Backend::ActiveRecord::Job) &&
76
+ Delayed::Worker.backend.name == Delayed::Backend::ActiveRecord::Job.name
77
+ end
78
+
79
+ # Hash of hashes containing all currently running jobs' start timestamps
80
+ # to calculate maximum durations of currently running not yet completed jobs
81
+ # { { queue: "default", worker: "SomeJob" } => { "jid1" => 100500, "jid2" => 424242 } }
82
+ attr_accessor :jobs_started_at
83
+
84
+ def track_max_job_runtime
85
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
86
+ ::Yabeda::DelayedJob.jobs_started_at.each do |labels, jobs|
87
+ oldest_job_started_at = jobs.values.min
88
+ oldest_job_duration = oldest_job_started_at ? (now - oldest_job_started_at).round(3) : 0
89
+ Yabeda.delayed_job.running_job_runtime.set(labels, oldest_job_duration)
90
+ end
91
+ end
92
+
93
+ def elapsed(start)
94
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).round(3)
95
+ end
96
+ end
97
+
98
+ self.jobs_started_at = Concurrent::Hash.new { |hash, key| hash[key] = Concurrent::Hash.new }
99
+
100
+ Delayed::Worker.plugins << ::Yabeda::DelayedJob::Plugin
101
+ end
102
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+
5
+ module Yabeda
6
+ module DelayedJob
7
+ class Plugin < ::Delayed::Plugin
8
+ callbacks do |lifecycle|
9
+ lifecycle.after(:enqueue) do |job|
10
+ labels = ::Yabeda::DelayedJob.labelize(job)
11
+ ::Yabeda.delayed_job.jobs_enqueued_total.increment(labels)
12
+ end
13
+
14
+ lifecycle.after(:failure) do |_worker, job|
15
+ labels = ::Yabeda::DelayedJob.labelize(job)
16
+ ::Yabeda.delayed_job.jobs_failed_total.increment(labels)
17
+ end
18
+
19
+ lifecycle.after(:error) do |_worker, job|
20
+ labels = ::Yabeda::DelayedJob.labelize(job)
21
+ ::Yabeda.delayed_job.jobs_errored_total.increment(labels)
22
+ end
23
+
24
+ lifecycle.around(:perform) do |worker, job, &block|
25
+ labels = ::Yabeda::DelayedJob.labelize(job)
26
+
27
+ Process.clock_gettime(Process::CLOCK_MONOTONIC).tap do |start|
28
+ Yabeda::DelayedJob.jobs_started_at[labels][job.id] = start
29
+
30
+ block.call(worker)
31
+
32
+ Yabeda.delayed_job.job_runtime.measure(
33
+ labels,
34
+ ::Yabeda::DelayedJob.elapsed(start)
35
+ )
36
+ end
37
+ ensure
38
+ Yabeda::DelayedJob.jobs_started_at[labels].delete(job.id)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yabeda
4
+ module DelayedJob
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'yabeda/delayed_job/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'yabeda-delayed_job'
9
+ spec.version = Yabeda::DelayedJob::VERSION
10
+ spec.authors = ['Dmitry']
11
+ spec.email = ['dsalahutdinov@gmail.com']
12
+
13
+ spec.summary = 'Monitoring for delayed_job background worker'
14
+ spec.description = 'Extends Yabeda to colleck delayed_job metrics'
15
+ spec.homepage = 'https://github.com/yabeda-rb/yabeda-delayed_job'
16
+ spec.license = 'MIT'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/yabeda-rb/yabeda-delayed_job'
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
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 'delayed_job'
31
+ spec.add_dependency 'yabeda'
32
+
33
+ spec.add_development_dependency 'bundler'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.0'
36
+ spec.add_development_dependency 'rubocop', '~> 1.15'
37
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yabeda-delayed_job
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dmitry
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: delayed_job
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yabeda
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.15'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.15'
97
+ description: Extends Yabeda to colleck delayed_job metrics
98
+ email:
99
+ - dsalahutdinov@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".rubocop.yml"
107
+ - ".travis.yml"
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - LICENSE
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - bin/console
116
+ - bin/setup
117
+ - docker-compose.yml
118
+ - docs/yabeda-delayed_job-grafana.png
119
+ - example/Gemfile
120
+ - example/Gemfile.lock
121
+ - example/delayed_job.rb
122
+ - example/docker-compose.yml
123
+ - example/enqueuer.rb
124
+ - example/grafana/dashboards/delayed_job.json
125
+ - example/grafana/grafana.ini
126
+ - example/grafana/provisioning/dashboards/delayed_job.yml
127
+ - example/grafana/provisioning/datasources/prometheus.yml
128
+ - example/jobs.rb
129
+ - example/log/delayed_job.log
130
+ - example/prometheus/config.yml
131
+ - example/prometheus_store.rb
132
+ - lib/yabeda/delayed_job.rb
133
+ - lib/yabeda/delayed_job/plugin.rb
134
+ - lib/yabeda/delayed_job/version.rb
135
+ - yabeda-delayed_job.gemspec
136
+ homepage: https://github.com/yabeda-rb/yabeda-delayed_job
137
+ licenses:
138
+ - MIT
139
+ metadata:
140
+ homepage_uri: https://github.com/yabeda-rb/yabeda-delayed_job
141
+ source_code_uri: https://github.com/yabeda-rb/yabeda-delayed_job
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubygems_version: 3.2.15
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Monitoring for delayed_job background worker
161
+ test_files: []