yabeda-sidekiq 0.1.4 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21651b2b4b71d0573f1fb8e97768bd45116ad895ebfbf93122b734e4ebcf00a5
4
- data.tar.gz: 217140bb32008e2082e1723c05953e36aa2a9f49a4a0b9941ca60ca982e25636
3
+ metadata.gz: 68116758a02a4497a4eda56cac585a5afcfac8902e880d1c061c70f952898ed0
4
+ data.tar.gz: 599d85f03c0a5cc8ef87c78db670337263cc1eecb4dd14ee0925aa0862b07e12
5
5
  SHA512:
6
- metadata.gz: 1644f7bcdd3af5ca75d848c1963bb27a0543b745c1d38cef1160449b0b534d647791d9207c4b6ec882ff6004283f8773bdae41aaaa1ae81bf428c809f3bd0b45
7
- data.tar.gz: aea64592cb596f91b30fb55228545f2d03499bff97081267e8b028a4b5d4510824b63cdc73d1d4b44824f1000d22ee98b73a756aa6fa2ac41fdf6bb1687844dc
6
+ metadata.gz: f86db733e71ce0b21e1997714124d55cb97774d3076e9d2c5d7b2a014f741973d185b92bcd3fc7eb34e57d1ee5de6a696266129663557a327f424b8eda308d30
7
+ data.tar.gz: ebfb0e1814c4e9b31f0fb130028fb764b8014688a0c70add91e04282d57de545dd32de37bcfa42d266d7defb081e016060007e90ce61908443234fcdcff557ba
@@ -0,0 +1,82 @@
1
+ name: Build and release gem to RubyGems
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ with:
14
+ fetch-depth: 0 # Fetch current tag as annotated. See https://github.com/actions/checkout/issues/290
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: 2.7
18
+ - name: "Extract data from tag: version, message, body"
19
+ id: tag
20
+ run: |
21
+ git fetch --tags --force # Really fetch annotated tag. See https://github.com/actions/checkout/issues/290#issuecomment-680260080
22
+ echo ::set-output name=version::${GITHUB_REF#refs/tags/v}
23
+ echo ::set-output name=subject::$(git for-each-ref $GITHUB_REF --format='%(contents:subject)')
24
+ BODY="$(git for-each-ref $GITHUB_REF --format='%(contents:body)')"
25
+ # Extract changelog entries between this and previous version headers
26
+ escaped_version=$(echo ${GITHUB_REF#refs/tags/v} | sed -e 's/[]\/$*.^[]/\\&/g')
27
+ changelog=$(awk "BEGIN{inrelease=0} /## ${escaped_version}/{inrelease=1;next} /## [0-9]+\.[0-9]+\.[0-9]+/{inrelease=0;exit} {if (inrelease) print}" CHANGELOG.md)
28
+ # Multiline body for release. See https://github.community/t/set-output-truncates-multiline-strings/16852/5
29
+ BODY="${BODY}"$'\n'"${changelog}"
30
+ BODY="${BODY//'%'/'%25'}"
31
+ BODY="${BODY//$'\n'/'%0A'}"
32
+ BODY="${BODY//$'\r'/'%0D'}"
33
+ echo "::set-output name=body::$BODY"
34
+ # Add pre-release option if tag name has any suffix after vMAJOR.MINOR.PATCH
35
+ if [[ ${GITHUB_REF#refs/tags/} =~ ^v[0-9]+\.[0-9]+\.[0-9]+.+ ]]; then
36
+ echo ::set-output name=prerelease::true
37
+ fi
38
+ - name: Build gem
39
+ run: gem build
40
+ - name: Calculate checksums
41
+ run: sha256sum yabeda-sidekiq-${{ steps.tag.outputs.version }}.gem > SHA256SUM
42
+ - name: Check version
43
+ run: ls -l yabeda-sidekiq-${{ steps.tag.outputs.version }}.gem
44
+ - name: Create Release
45
+ id: create_release
46
+ uses: actions/create-release@v1
47
+ env:
48
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49
+ with:
50
+ tag_name: ${{ github.ref }}
51
+ release_name: ${{ steps.tag.outputs.subject }}
52
+ body: ${{ steps.tag.outputs.body }}
53
+ draft: false
54
+ prerelease: ${{ steps.tag.outputs.prerelease }}
55
+ - name: Upload built gem as release asset
56
+ uses: actions/upload-release-asset@v1
57
+ env:
58
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59
+ with:
60
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
61
+ asset_path: yabeda-sidekiq-${{ steps.tag.outputs.version }}.gem
62
+ asset_name: yabeda-sidekiq-${{ steps.tag.outputs.version }}.gem
63
+ asset_content_type: application/x-tar
64
+ - name: Upload checksums as release asset
65
+ uses: actions/upload-release-asset@v1
66
+ env:
67
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68
+ with:
69
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
70
+ asset_path: SHA256SUM
71
+ asset_name: SHA256SUM
72
+ asset_content_type: text/plain
73
+ - name: Publish to GitHub packages
74
+ env:
75
+ GEM_HOST_API_KEY: Bearer ${{ secrets.GITHUB_TOKEN }}
76
+ run: |
77
+ gem push yabeda-sidekiq-${{ steps.tag.outputs.version }}.gem --host https://rubygems.pkg.github.com/${{ github.repository_owner }}
78
+ - name: Publish to RubyGems
79
+ env:
80
+ GEM_HOST_API_KEY: "${{ secrets.RUBYGEMS_API_KEY }}"
81
+ run: |
82
+ gem push yabeda-sidekiq-${{ steps.tag.outputs.version }}.gem
@@ -0,0 +1,47 @@
1
+ name: Run tests
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - '**'
8
+ tags-ignore:
9
+ - 'v*'
10
+
11
+ jobs:
12
+ test:
13
+ name: "Run tests"
14
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ include:
20
+ - ruby: 3.0
21
+ - ruby: 2.7
22
+ - ruby: 2.6
23
+ - ruby: 2.5
24
+ container:
25
+ image: ruby:${{ matrix.ruby }}
26
+ env:
27
+ CI: true
28
+ steps:
29
+ - uses: actions/checkout@v2
30
+ - uses: actions/cache@v2
31
+ with:
32
+ path: vendor/bundle
33
+ key: bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }}-${{ hashFiles('**/Gemfile') }}
34
+ restore-keys: |
35
+ bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }}-${{ hashFiles('**/Gemfile') }}
36
+ bundle-${{ matrix.ruby }}-
37
+ - name: Upgrade Bundler to 2.0 (for older Rubies)
38
+ run: gem install bundler -v '~> 2.0'
39
+ - name: Bundle install
40
+ run: |
41
+ bundle config path vendor/bundle
42
+ bundle install
43
+ bundle update
44
+ - name: Run Rubocop
45
+ run: bundle exec rubocop
46
+ - name: Run RSpec
47
+ run: bundle exec rspec
data/.rubocop.yml CHANGED
@@ -1,17 +1,18 @@
1
1
  ---
2
2
  require:
3
- - rubocop-rspec
3
+ - rubocop-rspec
4
4
 
5
5
  AllCops:
6
6
  TargetRubyVersion: 2.3
7
7
 
8
8
  Metrics/BlockLength:
9
+ Enabled: false
9
10
  Exclude:
10
11
  - "Gemfile"
11
12
  - "spec/**/*"
12
13
 
13
- Style/BracesAroundHashParameters:
14
- EnforcedStyle: context_dependent
14
+ Layout/LineLength:
15
+ Max: 160
15
16
 
16
17
  Style/StringLiterals:
17
18
  EnforcedStyle: double_quotes
@@ -44,3 +45,23 @@ Style/TrailingCommaInHashLiteral:
44
45
  Enabled: true
45
46
  EnforcedStyleForMultiline: consistent_comma
46
47
 
48
+ Style/HashEachMethods:
49
+ Enabled: true
50
+
51
+ Style/HashTransformKeys:
52
+ Enabled: true
53
+
54
+ Style/HashTransformValues:
55
+ Enabled: true
56
+
57
+ RSpec/ExampleLength:
58
+ Enabled: false
59
+
60
+ Style/Documentation:
61
+ Enabled: false
62
+
63
+ Metrics/MethodLength:
64
+ Max: 15
65
+
66
+ Metrics/AbcSize:
67
+ Max: 17
data/CHANGELOG.md CHANGED
@@ -5,6 +5,50 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## Unreleased
9
+
10
+ ## 0.8.0 - 2021-05-12
11
+
12
+ ### Added
13
+
14
+ - `sidekiq_running_job_runtime` metric that tracks maximum runtime of currently running jobs. It may be useful for detection of hung jobs. See [#17](https://github.com/yabeda-rb/yabeda-sidekiq/pull/17). [@dsalahutdinov], [@Envek]
15
+
16
+ - Setting `collect_cluster_metrics` allowing to force enable or disable collection of global (whole Sidekiq installaction-wide) metrics. See [#20](https://github.com/yabeda-rb/yabeda-sidekiq/pull/20). [@mrexox]
17
+
18
+ By default all sidekiq worker processes (servers) collects global metrics about whole Sidekiq installation.
19
+ Client processes (everything else that is not Sidekiq worker) by default doesn't.
20
+
21
+ With this config you can override this behavior:
22
+ - force disable if you don't want multiple Sidekiq workers to report the same numbers (that causes excess load to both Redis and monitoring)
23
+ - force enable if you want non-Sidekiq process to collect them (like dedicated metric exporter process)
24
+
25
+ ## 0.7.0 - 2020-07-15
26
+
27
+ ### Changed
28
+
29
+ - Tags from `yabeda_tags` method are applied to all metrics collected inside a job, not only sidekiq-specific. See [#14](https://github.com/yabeda-rb/yabeda-sidekiq/issues/14). @Envek
30
+
31
+ ## 0.6.0 - 2020-07-15
32
+
33
+ ### Added
34
+
35
+ - Ability to override or add tags for every job via `yabeda_tags` method. @Envek
36
+
37
+ ## 0.5.0 - 2020-02-20
38
+
39
+ ### Added
40
+
41
+ - New `sidekiq_job_latency` histogram to track latency statistics of different job classes. [#9](https://github.com/yabeda-rb/yabeda-sidekiq/pull/9) by [@asusikov]
42
+
43
+ ### Changed
44
+
45
+ - **BREAKING CHANGE!** Renamed `sidekiq_jobs_latency` gauge to `sidekiq_queue_latency` to better describe its purpose and differentiate with the new histogram. [#9](https://github.com/yabeda-rb/yabeda-sidekiq/pull/9) by [@asusikov]
46
+
47
+ ## 0.2.0 - 2020-01-14
48
+
49
+ ### Changed
50
+
51
+ - Added `tags` option to metric declarations for compatibility with yabeda and yabeda-prometheus 0.2. @Envek
8
52
 
9
53
  ## 0.1.4 - 2019-10-07
10
54
 
@@ -34,4 +78,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
34
78
 
35
79
  - Initial release of evil-metrics-sidekiq gem. @Envek
36
80
 
81
+ [@Envek]: https://github.com/Envek "Andrey Novikov"
37
82
  [@dsalahutdinov]: https://github.com/dsalahutdinov "Salahutdinov Dmitry"
83
+ [@asusikov]: https://github.com/asusikov "Alexander Susikov"
84
+ [@mrexox]: https://github.com/mrexox "Valentine Kiselev"
data/Gemfile CHANGED
@@ -11,6 +11,6 @@ group :development, :test do
11
11
  gem "pry"
12
12
  gem "pry-byebug", platform: :mri
13
13
 
14
- gem "rubocop"
14
+ gem "rubocop", "~> 0.80.0"
15
15
  gem "rubocop-rspec"
16
16
  end
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Built-in metrics for [Sidekiq] monitoring out of the box! Part of the [yabeda] suite.
4
4
 
5
+ Sample Grafana dashboard ID: [11667](https://grafana.com/grafana/dashboards/11667)
6
+
5
7
  ## Installation
6
8
 
7
9
  ```ruby
@@ -14,6 +16,14 @@ And then execute:
14
16
 
15
17
  $ bundle
16
18
 
19
+ If you're not on Rails then configure Yabeda after your application was initialized:
20
+
21
+ ```ruby
22
+ Yabeda.configure!
23
+ ```
24
+
25
+ _If you're using Ruby on Rails then Yabeda will configure itself automatically!_
26
+
17
27
  **And that is it!** Sidekiq metrics are being collected!
18
28
 
19
29
  Additionally, depending on your adapter, you may want to setup metrics export. E.g. for [yabeda-prometheus]:
@@ -27,17 +37,61 @@ end
27
37
 
28
38
  ## Metrics
29
39
 
40
+ ### Local per-process metrics
41
+
42
+ Metrics representing state of current Sidekiq worker process and stats of executed or executing jobs:
43
+
30
44
  - Total number of executed jobs: `sidekiq_jobs_executed_total` - (segmented by queue and class name)
31
45
  - Number of jobs have been finished successfully: `sidekiq_jobs_success_total` (segmented by queue and class name)
32
46
  - Number of jobs have been failed: `sidekiq_jobs_failed_total` (segmented by queue and class name)
33
47
  - Time of job run: `sidekiq_job_runtime` (seconds per job execution, segmented by queue and class name)
34
- - Time of the queue latency `sidekiq_jobs_latency` (the difference in seconds since the oldest job in the queue was enqueued)
48
+ - Time of the job latency `sidekiq_job_latency` (the difference in seconds since the enqueuing until running job)
49
+ - Maximum runtime of currently executing jobs: `sidekiq_running_job_runtime` (useful for detection of hung jobs, segmented by queue and class name)
50
+
51
+ ### Global cluster-wide metrics
52
+
53
+ Metrics representing state of the whole Sidekiq installation (queues, processes, etc):
54
+
35
55
  - Number of jobs in queues: `sidekiq_jobs_waiting_count` (segmented by queue)
56
+ - Time of the queue latency `sidekiq_queue_latency` (the difference in seconds since the oldest job in the queue was enqueued)
36
57
  - Number of scheduled jobs:`sidekiq_jobs_scheduled_count`
37
58
  - Number of jobs in retry set: `sidekiq_jobs_retry_count`
38
59
  - Number of jobs in dead set (“morgue”): `sidekiq_jobs_dead_count`
39
- - Active workers count: `sidekiq_active_processes`
40
- - Active processes count: `sidekiq_active_workers_count`
60
+ - Active processes count: `sidekiq_active_processes`
61
+ - Active servers count: `sidekiq_active_workers_count`
62
+
63
+ By default all sidekiq worker processes (servers) collects global metrics about whole Sidekiq installation. This can be overridden by setting `collect_cluster_metrics` config key to `true` for non-Sidekiq processes or to `false` for Sidekiq processes (e.g. by setting `YABEDA_SIDEKIQ_COLLECT_CLUSTER_METRICS` env variable to `no`, see other methods in [anyway_config] docs).
64
+
65
+ ## Custom tags
66
+
67
+ You can add additional tags to these metrics by declaring `yabeda_tags` method in your worker.
68
+
69
+ ```ruby
70
+ # This block is optional but some adapters (like Prometheus) requires that all tags should be declared in advance
71
+ Yabeda.configure do
72
+ default_tag :importance, nil
73
+ end
74
+
75
+ class MyWorker
76
+ include Sidekiq::Worker
77
+
78
+ def yabeda_tags(*params) # This method will be called first, before +perform+
79
+ { importance: extract_importance(params) }
80
+ end
81
+
82
+ def perform(*params)
83
+ # Your logic here
84
+ end
85
+ end
86
+ ```
87
+
88
+ ## Configuration
89
+
90
+ Configuration is handled by [anyway_config] gem. With it you can load settings from environment variables (upcased and prefixed with `YABEDA_SIDEKIQ_`), YAML files, and other sources. See [anyway_config] docs for details.
91
+
92
+ Config key | Type | Default | Description |
93
+ ------------------------- | -------- | ------------------------------------------------------- | ----------- |
94
+ `collect_cluster_metrics` | boolean | Enabled in Sidekiq worker processes, disabled otherwise | Defines whether this Ruby process should collect and expose metrics representing state of the whole Sidekiq installation (queues, processes, etc). |
41
95
 
42
96
  # Roadmap (TODO or Help wanted)
43
97
 
@@ -57,6 +111,38 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
57
111
 
58
112
  Bug reports and pull requests are welcome on GitHub at https://github.com/yabeda-rb/yabeda-sidekiq.
59
113
 
114
+ ### Releasing
115
+
116
+ 1. Bump version number in `lib/yabeda/sidekiq/version.rb`
117
+
118
+ In case of pre-releases keep in mind [rubygems/rubygems#3086](https://github.com/rubygems/rubygems/issues/3086) and check version with command like `Gem::Version.new(Yabeda::Sidekiq::VERSION).to_s`
119
+
120
+ 2. Fill `CHANGELOG.md` with missing changes, add header with version and date.
121
+
122
+ 3. Make a commit:
123
+
124
+ ```sh
125
+ git add lib/yabeda/sidekiq/version.rb CHANGELOG.md
126
+ version=$(ruby -r ./lib/yabeda/sidekiq/version.rb -e "puts Gem::Version.new(Yabeda::Sidekiq::VERSION)")
127
+ git commit --message="${version}: " --edit
128
+ ```
129
+
130
+ 4. Create annotated tag:
131
+
132
+ ```sh
133
+ git tag v${version} --annotate --message="${version}: " --edit --sign
134
+ ```
135
+
136
+ 5. Fill version name into subject line and (optionally) some description (list of changes will be taken from changelog and appended automatically)
137
+
138
+ 6. Push it:
139
+
140
+ ```sh
141
+ git push --follow-tags
142
+ ```
143
+
144
+ 7. GitHub Actions will create a new release, build and push gem into RubyGems! You're done!
145
+
60
146
  ## License
61
147
 
62
148
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -64,3 +150,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
64
150
  [Sidekiq]: https://github.com/mperham/sidekiq/ "Simple, efficient background processing for Ruby"
65
151
  [yabeda]: https://github.com/yabeda-rb/yabeda
66
152
  [yabeda-prometheus]: https://github.com/yabeda-rb/yabeda-prometheus
153
+ [anyway_config]: https://github.com/palkan/anyway_config "Configuration library for Ruby gems and applications"
data/Rakefile CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
+ require "rubocop/rake_task"
6
+
7
+ RuboCop::RakeTask.new
5
8
 
6
9
  RSpec::Core::RakeTask.new(:spec)
7
10
 
8
- task default: :spec
11
+ task default: %i[rubocop spec]
@@ -7,36 +7,57 @@ require "yabeda"
7
7
  require "yabeda/sidekiq/version"
8
8
  require "yabeda/sidekiq/client_middleware"
9
9
  require "yabeda/sidekiq/server_middleware"
10
+ require "yabeda/sidekiq/config"
10
11
 
11
12
  module Yabeda
12
13
  module Sidekiq
13
14
  LONG_RUNNING_JOB_RUNTIME_BUCKETS = [
14
15
  0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, # standard (from Prometheus)
15
- 30, 60, 120, 300, 1800, 3600, 21_600 # Sidekiq tasks may be very long-running
16
+ 30, 60, 120, 300, 1800, 3600, 21_600, # Sidekiq tasks may be very long-running
16
17
  ].freeze
17
18
 
18
19
  Yabeda.configure do
20
+ config = Config.new
21
+
19
22
  group :sidekiq
20
23
 
21
- counter :jobs_enqueued_total, comment: "A counter of the total number of jobs sidekiq enqueued."
24
+ counter :jobs_enqueued_total, tags: %i[queue worker], comment: "A counter of the total number of jobs sidekiq enqueued."
25
+
26
+ if ::Sidekiq.server?
27
+ counter :jobs_executed_total, tags: %i[queue worker], comment: "A counter of the total number of jobs sidekiq executed."
28
+ counter :jobs_success_total, tags: %i[queue worker], comment: "A counter of the total number of jobs successfully processed by sidekiq."
29
+ counter :jobs_failed_total, tags: %i[queue worker], comment: "A counter of the total number of jobs failed in sidekiq."
22
30
 
23
- next unless ::Sidekiq.server?
31
+ gauge :running_job_runtime, tags: %i[queue worker], aggregation: :max, unit: :seconds,
32
+ comment: "How long currently running jobs are running (useful for detection of hung jobs)"
24
33
 
25
- counter :jobs_executed_total, comment: "A counter of the total number of jobs sidekiq executed."
26
- counter :jobs_success_total, comment: "A counter of the total number of jobs successfully processed by sidekiq."
27
- counter :jobs_failed_total, comment: "A counter of the total number of jobs failed in sidekiq."
28
- gauge :jobs_waiting_count, comment: "The number of jobs waiting to process in sidekiq."
29
- gauge :active_workers_count, comment: "The number of currently running machines with sidekiq workers."
30
- gauge :jobs_scheduled_count, comment: "The number of jobs scheduled for later execution."
31
- gauge :jobs_retry_count, comment: "The number of failed jobs waiting to be retried"
32
- gauge :jobs_dead_count, comment: "The number of jobs exceeded their retry count."
33
- gauge :active_processes, comment: "The number of active Sidekiq worker processes."
34
- gauge :jobs_latency, comment: "The job latency, the difference in seconds since the oldest job in the queue was enqueued"
34
+ histogram :job_latency, comment: "The job latency, the difference in seconds between enqueued and running time",
35
+ unit: :seconds, per: :job,
36
+ tags: %i[queue worker],
37
+ buckets: LONG_RUNNING_JOB_RUNTIME_BUCKETS
38
+ histogram :job_runtime, comment: "A histogram of the job execution time.",
39
+ unit: :seconds, per: :job,
40
+ tags: %i[queue worker],
41
+ buckets: LONG_RUNNING_JOB_RUNTIME_BUCKETS
42
+ end
35
43
 
36
- histogram :job_runtime, unit: :seconds, per: :job, comment: "A histogram of the job execution time.",
37
- buckets: LONG_RUNNING_JOB_RUNTIME_BUCKETS
44
+ # Metrics not specific for current Sidekiq process, but representing state of the whole Sidekiq installation (queues, processes, etc)
45
+ # You can opt-out from collecting these by setting YABEDA_SIDEKIQ_COLLECT_CLUSTER_METRICS to falsy value (+no+ or +false+)
46
+ if config.collect_cluster_metrics # defaults to +::Sidekiq.server?+
47
+ gauge :jobs_waiting_count, tags: %i[queue], comment: "The number of jobs waiting to process in sidekiq."
48
+ gauge :active_workers_count, tags: [], comment: "The number of currently running machines with sidekiq workers."
49
+ gauge :jobs_scheduled_count, tags: [], comment: "The number of jobs scheduled for later execution."
50
+ gauge :jobs_retry_count, tags: [], comment: "The number of failed jobs waiting to be retried"
51
+ gauge :jobs_dead_count, tags: [], comment: "The number of jobs exceeded their retry count."
52
+ gauge :active_processes, tags: [], comment: "The number of active Sidekiq worker processes."
53
+ gauge :queue_latency, tags: %i[queue], comment: "The queue latency, the difference in seconds since the oldest job in the queue was enqueued"
54
+ end
38
55
 
39
56
  collect do
57
+ Yabeda::Sidekiq.track_max_job_runtime if ::Sidekiq.server?
58
+
59
+ next unless config.collect_cluster_metrics
60
+
40
61
  stats = ::Sidekiq::Stats.new
41
62
 
42
63
  stats.queues.each do |k, v|
@@ -49,7 +70,7 @@ module Yabeda
49
70
  sidekiq_jobs_retry_count.set({}, stats.retry_size)
50
71
 
51
72
  ::Sidekiq::Queue.all.each do |queue|
52
- sidekiq_jobs_latency.set({ queue: queue.name }, queue.latency)
73
+ sidekiq_queue_latency.set({ queue: queue.name }, queue.latency)
53
74
  end
54
75
 
55
76
  # That is quite slow if your retry set is large
@@ -86,10 +107,34 @@ module Yabeda
86
107
 
87
108
  def worker_class(worker, job)
88
109
  if defined?(ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper)
89
- return job["wrapped"] if worker.is_a?(ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper)
110
+ if worker.is_a?(ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper) || worker == ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper
111
+ return job["wrapped"].to_s
112
+ end
113
+ end
114
+ (worker.is_a?(String) || worker.is_a?(Class) ? worker : worker.class).to_s
115
+ end
116
+
117
+ def custom_tags(worker, job)
118
+ return {} unless worker.respond_to?(:yabeda_tags)
119
+
120
+ worker.method(:yabeda_tags).arity.zero? ? worker.yabeda_tags : worker.yabeda_tags(*job["args"])
121
+ end
122
+
123
+ # Hash of hashes containing all currently running jobs' start timestamps
124
+ # to calculate maximum durations of currently running not yet completed jobs
125
+ # { { queue: "default", worker: "SomeJob" } => { "jid1" => 100500, "jid2" => 424242 } }
126
+ attr_accessor :jobs_started_at
127
+
128
+ def track_max_job_runtime
129
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
130
+ ::Yabeda::Sidekiq.jobs_started_at.each do |labels, jobs|
131
+ oldest_job_started_at = jobs.values.min
132
+ oldest_job_duration = oldest_job_started_at ? (now - oldest_job_started_at).round(3) : 0
133
+ Yabeda.sidekiq.running_job_runtime.set(labels, oldest_job_duration)
90
134
  end
91
- (worker.is_a?(String) ? worker : worker.class).to_s
92
135
  end
93
136
  end
137
+
138
+ self.jobs_started_at = Concurrent::Hash.new { |hash, key| hash[key] = Concurrent::Hash.new }
94
139
  end
95
140
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyway"
4
+
5
+ module Yabeda
6
+ module Sidekiq
7
+ class Config < ::Anyway::Config
8
+ config_name :yabeda_sidekiq
9
+
10
+ # By default all sidekiq worker processes (servers) collects global metrics about whole Sidekiq installation.
11
+ # Client processes (everything else that is not Sidekiq worker) by default doesn't.
12
+ # With this config you can override this behavior:
13
+ # - force disable if you don't want multiple Sidekiq workers to report the same numbers (that causes excess load to both Redis and monitoring)
14
+ # - force enable if you want non-Sidekiq process to collect them (like dedicated metric exporter process)
15
+ attr_config collect_cluster_metrics: ::Sidekiq.server?
16
+ end
17
+ end
18
+ end
@@ -4,11 +4,18 @@ module Yabeda
4
4
  module Sidekiq
5
5
  # Sidekiq worker middleware
6
6
  class ServerMiddleware
7
+ # rubocop: disable Metrics/AbcSize, Metrics/MethodLength:
7
8
  def call(worker, job, queue)
8
- labels = Yabeda::Sidekiq.labelize(worker, job, queue)
9
- start = Time.now
9
+ custom_tags = Yabeda::Sidekiq.custom_tags(worker, job).to_h
10
+ labels = Yabeda::Sidekiq.labelize(worker, job, queue).merge(custom_tags)
11
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
10
12
  begin
11
- yield
13
+ job_instance = ::Sidekiq::Job.new(job)
14
+ Yabeda.sidekiq_job_latency.measure(labels, job_instance.latency)
15
+ Yabeda::Sidekiq.jobs_started_at[labels][job["jid"]] = start
16
+ Yabeda.with_tags(**custom_tags) do
17
+ yield
18
+ end
12
19
  Yabeda.sidekiq_jobs_success_total.increment(labels)
13
20
  rescue Exception # rubocop: disable Lint/RescueException
14
21
  Yabeda.sidekiq_jobs_failed_total.increment(labels)
@@ -16,13 +23,15 @@ module Yabeda
16
23
  ensure
17
24
  Yabeda.sidekiq_job_runtime.measure(labels, elapsed(start))
18
25
  Yabeda.sidekiq_jobs_executed_total.increment(labels)
26
+ Yabeda::Sidekiq.jobs_started_at[labels].delete(job["jid"])
19
27
  end
20
28
  end
29
+ # rubocop: enable Metrics/AbcSize, Metrics/MethodLength:
21
30
 
22
31
  private
23
32
 
24
33
  def elapsed(start)
25
- (Time.now - start).round(3)
34
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).round(3)
26
35
  end
27
36
  end
28
37
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Yabeda
4
4
  module Sidekiq
5
- VERSION = "0.1.4"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
@@ -22,10 +22,12 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_dependency "yabeda"
25
+ spec.add_dependency "anyway_config", ">= 1.3", "< 3"
26
26
  spec.add_dependency "sidekiq"
27
+ spec.add_dependency "yabeda", "~> 0.6"
27
28
 
28
- spec.add_development_dependency "bundler", "~> 1.16"
29
- spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "activejob", ">= 6.0"
30
+ spec.add_development_dependency "bundler", "~> 2.0"
31
+ spec.add_development_dependency "rake", "~> 13.0"
30
32
  spec.add_development_dependency "rspec", "~> 3.0"
31
33
  end
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yabeda-sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Novikov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-07 00:00:00.000000000 Z
11
+ date: 2021-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: yabeda
14
+ name: anyway_config
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.3'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: '1.3'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: sidekiq
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -38,34 +44,62 @@ dependencies:
38
44
  - - ">="
39
45
  - !ruby/object:Gem::Version
40
46
  version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: yabeda
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.6'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.6'
61
+ - !ruby/object:Gem::Dependency
62
+ name: activejob
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '6.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '6.0'
41
75
  - !ruby/object:Gem::Dependency
42
76
  name: bundler
43
77
  requirement: !ruby/object:Gem::Requirement
44
78
  requirements:
45
79
  - - "~>"
46
80
  - !ruby/object:Gem::Version
47
- version: '1.16'
81
+ version: '2.0'
48
82
  type: :development
49
83
  prerelease: false
50
84
  version_requirements: !ruby/object:Gem::Requirement
51
85
  requirements:
52
86
  - - "~>"
53
87
  - !ruby/object:Gem::Version
54
- version: '1.16'
88
+ version: '2.0'
55
89
  - !ruby/object:Gem::Dependency
56
90
  name: rake
57
91
  requirement: !ruby/object:Gem::Requirement
58
92
  requirements:
59
93
  - - "~>"
60
94
  - !ruby/object:Gem::Version
61
- version: '10.0'
95
+ version: '13.0'
62
96
  type: :development
63
97
  prerelease: false
64
98
  version_requirements: !ruby/object:Gem::Requirement
65
99
  requirements:
66
100
  - - "~>"
67
101
  - !ruby/object:Gem::Version
68
- version: '10.0'
102
+ version: '13.0'
69
103
  - !ruby/object:Gem::Dependency
70
104
  name: rspec
71
105
  requirement: !ruby/object:Gem::Requirement
@@ -88,10 +122,11 @@ executables: []
88
122
  extensions: []
89
123
  extra_rdoc_files: []
90
124
  files:
125
+ - ".github/workflows/build-release.yml"
126
+ - ".github/workflows/test.yml"
91
127
  - ".gitignore"
92
128
  - ".rspec"
93
129
  - ".rubocop.yml"
94
- - ".travis.yml"
95
130
  - CHANGELOG.md
96
131
  - Gemfile
97
132
  - LICENSE.txt
@@ -101,6 +136,7 @@ files:
101
136
  - bin/setup
102
137
  - lib/yabeda/sidekiq.rb
103
138
  - lib/yabeda/sidekiq/client_middleware.rb
139
+ - lib/yabeda/sidekiq/config.rb
104
140
  - lib/yabeda/sidekiq/server_middleware.rb
105
141
  - lib/yabeda/sidekiq/version.rb
106
142
  - yabeda-sidekiq.gemspec
@@ -123,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
159
  - !ruby/object:Gem::Version
124
160
  version: '0'
125
161
  requirements: []
126
- rubygems_version: 3.0.3
162
+ rubygems_version: 3.1.6
127
163
  signing_key:
128
164
  specification_version: 4
129
165
  summary: Extensible Prometheus exporter for monitoring your Sidekiq
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.5.1
5
- before_install: gem install bundler -v 1.16.1