sidekiq-cloudwatchmetrics 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0d12e8127fc1e085dfafd8812c3fca0ca8f60d84a18d0c1075df44f3d73dfa7e
4
+ data.tar.gz: d122fa6c24b657283c383e00a64be1d6d61ec1f22f6eabb1eb42d14b590992b9
5
+ SHA512:
6
+ metadata.gz: 86028f3e083b24983f2bf59a850e996c76bd6a0f09c200bcecb386ab914dd5fc48c70ba1fb2ac44edee0db2c22b190eeac79f46f6b609195a067ee7665841218
7
+ data.tar.gz: 9a0119a1004c93d68837980ed58103fe293026e89e37ef1d3d39312bdccf038152c73812f0189c1fbb82a713514804ab7e711374086bc2082c7ac6fa96d5bc0f
@@ -0,0 +1,2 @@
1
+ SSf�`Q�ҹ4����Uz<)� �M�B쭴��mˬf�*�S�$/
2
+ ��@���4�[�:k ����>�,T� n�L�NJ)%ф�t�������a�������C��ҙ\`�x�*�G���uV+�)XdH�ν��+�G �*ܖ����v5A���3׼+�a¤>,���N���]��,O�R��r��c�^�kVz ���:ri� ꟰Y9H�?�8"z�%�]S �-f%�tJ����\c�e�E[�G��R
Binary file
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Samuel Cochran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # Sidekiq CloudWatch Metrics
2
+
3
+ Runs a thread inside your Sidekiq processes to report metrics to CloudWatch
4
+ useful for autoscaling and keeping an eye on your queues.
5
+
6
+ Optimised for Sidekiq Enterprise with leader election, but works everywhere!
7
+
8
+ ## Installation
9
+
10
+ Add this gem to your application’s Gemfile near sidekiq and then run `bundle install`:
11
+
12
+ ```ruby
13
+ gem "sidekiq"
14
+ gem "sidekiq-cloudwatchmetrics"
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Add near your Sidekiq configuration, like in `config/initializers/sidekiq.rb` in Rails:
20
+
21
+ ```ruby
22
+ require "sidekiq"
23
+ require "sidekiq/cloudwatchmetrics"
24
+
25
+ Sidekiq::CloudWatchMetrics.enable!
26
+ ```
27
+
28
+ By default this assumes you're running on an EC2 instance with an instance role
29
+ that can publish CloudWatch metrics, or that you've supplied AWS credentials
30
+ through environment variables that aws-sdk expects. You can also explicitly
31
+ supply an [aws-sdk CloudWatch Client instance][cwclient]:
32
+
33
+ ```ruby
34
+ Sidekiq::CloudWatchMetrics.enable!(client: AWS::CloudWatch::Client.new)
35
+ ```
36
+
37
+ [cwclient]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudWatch/Client.html
38
+
39
+ ## Development
40
+
41
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
42
+
43
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
44
+
45
+ ## Contributing
46
+
47
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sj26/sidekiq-cloudwatchmetrics.
48
+
49
+ ## License
50
+
51
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
52
+
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq"
4
+ require "sidekiq/api"
5
+ require "sidekiq/util"
6
+
7
+ require "aws-sdk-cloudwatch"
8
+
9
+ module Sidekiq::CloudWatchMetrics
10
+ def self.enable!(**kwargs)
11
+ Sidekiq.configure_server do |config|
12
+ publisher = Publisher.new(**kwargs)
13
+
14
+ if Sidekiq.options[:lifecycle_events].has_key?(:leader)
15
+ # Only publish metrics on the leader if we have a leader (sidekiq-ent)
16
+ config.on(:leader) do
17
+ publisher.start
18
+ end
19
+ else
20
+ # Otherwise pubishing from every node doesn't hurt, it's just wasteful
21
+ config.on(:startup) do
22
+ publisher.start
23
+ end
24
+ end
25
+
26
+ config.on(:quiet) do
27
+ publisher.quiet if publisher.running?
28
+ end
29
+
30
+ config.on(:shutdown) do
31
+ publisher.stop if publisher.running?
32
+ end
33
+ end
34
+ end
35
+
36
+ class Publisher
37
+ include Sidekiq::Util
38
+
39
+ INTERVAL = 60 # seconds
40
+
41
+ def initialize(client: Aws::CloudWatch::Client.new)
42
+ @client = client
43
+ end
44
+
45
+ def start
46
+ logger.info { "Starting Sidekiq CloudWatch Metrics Publisher" }
47
+
48
+ @done = false
49
+ @thread = safe_thread("cloudwatch metrics publisher", &method(:run))
50
+ end
51
+
52
+ def running?
53
+ !@thread.nil? && @thread.alive?
54
+ end
55
+
56
+ def run
57
+ logger.info { "Started Sidekiq CloudWatch Metrics Publisher" }
58
+
59
+ # Publish stats every INTERVAL seconds, sleeping as required between runs
60
+ now = Time.now.to_f
61
+ tick = now
62
+ until @stop
63
+ logger.info { "Publishing Sidekiq CloudWatch Metrics" }
64
+ publish
65
+
66
+ now = Time.now.to_f
67
+ tick = [tick + INTERVAL, now].max
68
+ sleep(tick - now) if tick > now
69
+ end
70
+
71
+ logger.info { "Stopped Sidekiq CloudWatch Metrics Publisher" }
72
+ end
73
+
74
+ def publish
75
+ now = Time.now
76
+ stats = Sidekiq::Stats.new
77
+ processes = Sidekiq::ProcessSet.new.to_enum(:each).to_a
78
+ utilization = calculate_utilization(processes)
79
+ capacity = calculate_capacity(processes)
80
+ queues = stats.queues
81
+
82
+ metrics = [
83
+ {
84
+ metric_name: "ProcessedJobs",
85
+ timestamp: now,
86
+ value: stats.processed,
87
+ unit: "Count",
88
+ },
89
+ {
90
+ metric_name: "FailedJobs",
91
+ timestamp: now,
92
+ value: stats.failed,
93
+ unit: "Count",
94
+ },
95
+ {
96
+ metric_name: "EnqueuedJobs",
97
+ timestamp: now,
98
+ value: stats.enqueued,
99
+ unit: "Count",
100
+ },
101
+ {
102
+ metric_name: "ScheduledJobs",
103
+ timestamp: now,
104
+ value: stats.scheduled_size,
105
+ unit: "Count",
106
+ },
107
+ {
108
+ metric_name: "RetryJobs",
109
+ timestamp: now,
110
+ value: stats.retry_size,
111
+ unit: "Count",
112
+ },
113
+ {
114
+ metric_name: "DeadJobs",
115
+ timestamp: now,
116
+ value: stats.dead_size,
117
+ unit: "Count",
118
+ },
119
+ {
120
+ metric_name: "Workers",
121
+ timestamp: now,
122
+ value: stats.workers_size,
123
+ unit: "Count",
124
+ },
125
+ {
126
+ metric_name: "Processes",
127
+ timestamp: now,
128
+ value: stats.processes_size,
129
+ unit: "Count",
130
+ },
131
+ {
132
+ metric_name: "Capacity",
133
+ timestamp: now,
134
+ value: capacity,
135
+ unit: "Count",
136
+ },
137
+ {
138
+ metric_name: "Utilization",
139
+ timestamp: now,
140
+ value: utilization * 100.0,
141
+ unit: "Percent",
142
+ },
143
+ {
144
+ metric_name: "DefaultQueueLatency",
145
+ timestamp: now,
146
+ value: stats.default_queue_latency,
147
+ unit: "Seconds",
148
+ },
149
+ ]
150
+
151
+ queues.map do |(queue_name, queue_size)|
152
+ metrics << {
153
+ metric_name: "QueueSize",
154
+ dimensions: [{name: "QueueName", value: queue_name}],
155
+ timestamp: now,
156
+ value: queue_size,
157
+ unit: "Count",
158
+ }
159
+
160
+ queue_latency = Sidekiq::Queue.new(queue_name).latency
161
+
162
+ metrics << {
163
+ metric_name: "QueueLatency",
164
+ dimensions: [{name: "QueueName", value: queue_name}],
165
+ timestamp: now,
166
+ value: queue_latency,
167
+ unit: "Seconds",
168
+ }
169
+ end
170
+
171
+ # We can only put 20 metrics at a time
172
+ metrics.each_slice(20) do |some_metrics|
173
+ @client.put_metric_data(
174
+ namespace: "Sidekiq",
175
+ metric_data: some_metrics,
176
+ )
177
+ end
178
+ end
179
+
180
+ # Returns the total number of workers across all processes
181
+ private def calculate_capacity(processes)
182
+ processes.map do |process|
183
+ process["concurrency"]
184
+ end.sum
185
+ end
186
+
187
+ # Returns busy / concurrency averaged across processes (for scaling)
188
+ private def calculate_utilization(processes)
189
+ processes.map do |process|
190
+ process["busy"] / process["concurrency"].to_f
191
+ end.sum / processes.size.to_f
192
+ end
193
+
194
+ def quiet
195
+ logger.info { "Quieting Sidekiq CloudWatch Metrics Publisher" }
196
+ @stop = true
197
+ end
198
+
199
+ def stop
200
+ logger.info { "Stopping Sidekiq CloudWatch Metrics Publisher" }
201
+ @stop = true
202
+ @thread.wakeup
203
+ @thread.join
204
+ end
205
+ end
206
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq-cloudwatchmetrics
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Cochran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDKDCCAhCgAwIBAgIBBjANBgkqhkiG9w0BAQsFADA6MQ0wCwYDVQQDDARzajI2
14
+ MRQwEgYKCZImiZPyLGQBGRYEc2oyNjETMBEGCgmSJomT8ixkARkWA2NvbTAeFw0x
15
+ ODA4MTYwNTE4NDBaFw0xOTA4MTYwNTE4NDBaMDoxDTALBgNVBAMMBHNqMjYxFDAS
16
+ BgoJkiaJk/IsZAEZFgRzajI2MRMwEQYKCZImiZPyLGQBGRYDY29tMIIBIjANBgkq
17
+ hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr60Eo/ttCk8GMTMFiPr3GoYMIMFvLak
18
+ xSmTk9YGCB6UiEePB4THSSA5w6IPyeaCF/nWkDp3/BAam0eZMWG1IzYQB23TqIM0
19
+ 1xzcNRvFsn0aQoQ00k+sj+G83j3T5OOV5OZIlu8xAChMkQmiPd1NXc6uFv+Iacz7
20
+ kj+CMsI9YUFdNoU09QY0b+u+Rb6wDYdpyvN60YC30h0h1MeYbvYZJx/iZK4XY5zu
21
+ 4O/FL2ChjL2CPCpLZW55ShYyrzphWJwLOJe+FJ/ZBl6YXwrzQM9HKnt4titSNvyU
22
+ KzE3L63A3PZvExzLrN9u09kuWLLJfXB2sGOlw3n9t72rJiuBr3/OQQIDAQABozkw
23
+ NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU99dfRjEKFyczTeIz
24
+ m3ZsDWrNC80wDQYJKoZIhvcNAQELBQADggEBAEwUQFizoyvm/1/iX8i/+oK6hr5m
25
+ wd7ZEzIV32JRyaqkPFeWjBylRXZyoXsE2u1H9ofIw8b/5/M+uMIfR8jlJSuulGB7
26
+ rs3ixWpkwRW8Knz2A5zufM3uPypwDYbEUbTdhzqY9hCzCz4moHu2E5f5bz7zrp+g
27
+ kpFjMoo851LRGr5vEQMZ/U+Paq8ROLzAiOa72mNxIFzvnGcSDyBVixdewi7HoZWD
28
+ ccrr0sGoLnfK6DappOwH1I5uPvQohOATNmmU0MSSWOeiggLlEWmUYE7CvzoMc8R6
29
+ DlumKi5y9wE+XnxtyLbe6GlT3AkVGnhwPdwq2QBFLEWkTVogdCYomBCKfjY=
30
+ -----END CERTIFICATE-----
31
+ date: 2018-08-16 00:00:00.000000000 Z
32
+ dependencies:
33
+ - !ruby/object:Gem::Dependency
34
+ name: sidekiq
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: aws-sdk-cloudwatch
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.6'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.6'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.16'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.16'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '10.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.7'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.7'
103
+ - !ruby/object:Gem::Dependency
104
+ name: timecop
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.9'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.9'
117
+ description: |
118
+ Runs a thread inside your Sidekiq processes to report metrics to CloudWatch
119
+ useful for autoscaling and keeping an eye on your queues.
120
+
121
+ Optimised for Sidekiq Enterprise with leader election, but works everywhere!
122
+ email: sj26@sj26.com
123
+ executables: []
124
+ extensions: []
125
+ extra_rdoc_files: []
126
+ files:
127
+ - LICENSE
128
+ - README.md
129
+ - lib/sidekiq/cloudwatchmetrics.rb
130
+ homepage: https://github.com/sj26/sidekiq-cloudwatchmetrics
131
+ licenses:
132
+ - MIT
133
+ metadata: {}
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '2.4'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.7.7
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Publish Sidekiq metrics to AWS CloudWatch
154
+ test_files: []
@@ -0,0 +1,2 @@
1
+
2
+ ɴXBLX(D�B%ș��œٯ<0�v���xj[�@ʯo�J�W�醾~xn3"����=x��,�蔼�LF�>�b`�X��C�hH&