sidekiq-amigo 1.5.0 → 1.6.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 +4 -4
- data/lib/amigo/autoscaler/heroku.rb +146 -0
- data/lib/amigo/autoscaler.rb +156 -39
- data/lib/amigo/version.rb +1 -1
- metadata +40 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c51def3cb58812e0889c0768708747aefd3956495692ffcbb6521a1d24c6d0b0
|
4
|
+
data.tar.gz: 5081debc483ce040e8c6f3a26cfacdd85094c5e70895b390f23f58f795663da3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b126004a168ea18f4842bd34268fc3edb263d21de40731d178e9bd4a5db64bf403da0953b6083ea0e5c5e8d32bc9aa4faf0e66e54f0f166a077780d6573565f6
|
7
|
+
data.tar.gz: 4a710ac1029e607ed60b1f448e4c443d287bcb599252801a70a17a9478e0f5c80b373f610a14034be1d2d3c98541512e64ab2f8b2bc85869b70fbc009308007d
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "platform-api"
|
4
|
+
|
5
|
+
require "amigo/autoscaler"
|
6
|
+
|
7
|
+
module Amigo
|
8
|
+
class Autoscaler
|
9
|
+
# Autoscaler to use on Heroku, that starts additional worker processes when there is a high latency event
|
10
|
+
# and scales them down after the event is finished.
|
11
|
+
#
|
12
|
+
# When the first call of a high latency event happens (depth: 1), this class
|
13
|
+
# will ask Heroku how many dynos are in the formation. This is known as +active_event_initial_workers+.
|
14
|
+
#
|
15
|
+
# If +active_event_initial_workers+ is 0, no autoscaling will be done.
|
16
|
+
# This avoids a situation where a high latency event is triggered
|
17
|
+
# due to workers being deprovisioned intentionally, perhaps for maintenance.
|
18
|
+
#
|
19
|
+
# Each time the alert fires (see +Amigo::Autoscaler#alert_interval+),
|
20
|
+
# an additional worker will be added to the formation, up to +max_additional_workers+.
|
21
|
+
# So with +active_event_initial_workers+ of 1 and +max_additional_workers+ of 2,
|
22
|
+
# the first time the alert times, the formation will be set to 2 workers.
|
23
|
+
# The next time, it'll be set to 3 workers.
|
24
|
+
# After that, no additional workers will be provisioned.
|
25
|
+
#
|
26
|
+
# After the high latency event resolves,
|
27
|
+
# the dyno formation is restored to +active_event_initial_workers+.
|
28
|
+
#
|
29
|
+
# To use:
|
30
|
+
#
|
31
|
+
# heroku = PlatformAPI.connect_oauth(heroku_oauth_token)
|
32
|
+
# heroku_scaler = Amigo::Autoscaler::Heroku.new(heroku:, default_workers: 1)
|
33
|
+
# Amigo::Autoscaler.new(
|
34
|
+
# handlers: [heroku_scaler.alert_callback],
|
35
|
+
# latency_restored_handlers: [heroku_scaler.restored_callback],
|
36
|
+
# )
|
37
|
+
#
|
38
|
+
# See instance attributes for additional options.
|
39
|
+
#
|
40
|
+
# Note that this class is provided as an example, and potentially a base or implementation class.
|
41
|
+
# Your actual implementation may also want to alert when a max depth or duration is reached,
|
42
|
+
# since it can indicate a bigger problem. Autoscaling, especially of workers, is a tough problem
|
43
|
+
# without a one-size-fits-all approach.
|
44
|
+
class Heroku
|
45
|
+
# Heroku client, usually created via PlatformAPI.oauth_connect.
|
46
|
+
# @return [PlatformAPI::Client]
|
47
|
+
attr_reader :heroku
|
48
|
+
|
49
|
+
# Captured at the start of a high latency event.
|
50
|
+
# Nil otherwise.
|
51
|
+
# @return [Integer]
|
52
|
+
attr_reader :active_event_initial_workers
|
53
|
+
|
54
|
+
# Maximum number of workers to add.
|
55
|
+
#
|
56
|
+
# As the 'depth' of the alert is increased,
|
57
|
+
# workers are added to the recorded worker count until the max is reached.
|
58
|
+
# By default, this is 2 (so the max workers will be the recorded number, plus 2).
|
59
|
+
# Do not set this too high, since it can for example exhaust database connections or just end up
|
60
|
+
# increasing load.
|
61
|
+
#
|
62
|
+
# See class docs for more information.
|
63
|
+
# @return [Integer]
|
64
|
+
attr_reader :max_additional_workers
|
65
|
+
|
66
|
+
# Defaults to HEROKU_APP_NAME, which should already be set if you use Heroku dyna metadata,
|
67
|
+
# as per https://devcenter.heroku.com/articles/dyno-metadata.
|
68
|
+
# This must be provided if the env var is missing.
|
69
|
+
# @return [String]
|
70
|
+
attr_reader :app_id_or_app_name
|
71
|
+
|
72
|
+
# Defaults to 'worker', which is what you'll probably use if you have a simple system.
|
73
|
+
# If you use multiple worker processes for different queues, this class probably isn't sufficient.
|
74
|
+
# You will probably need to look at the slow queue names and determine the formation name to scale up.
|
75
|
+
# @return [String]
|
76
|
+
attr_reader :formation_id_or_formation_type
|
77
|
+
|
78
|
+
def initialize(
|
79
|
+
heroku:,
|
80
|
+
max_additional_workers: 2,
|
81
|
+
app_id_or_app_name: ENV.fetch("HEROKU_APP_NAME"),
|
82
|
+
formation_id_or_formation_type: "worker"
|
83
|
+
)
|
84
|
+
|
85
|
+
@heroku = heroku
|
86
|
+
@max_additional_workers = max_additional_workers
|
87
|
+
@app_id_or_app_name = app_id_or_app_name
|
88
|
+
@formation_id_or_formation_type = formation_id_or_formation_type
|
89
|
+
# Is nil outside of a latency event, set during a latency event. So if this is initialized to non-nil,
|
90
|
+
# we're already in a latency event.
|
91
|
+
@active_event_initial_workers = Sidekiq.redis do |r|
|
92
|
+
v = r.get("#{namespace}/active_event_initial_workers")
|
93
|
+
v&.to_i
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def alert_callback
|
98
|
+
self.method(:scale_up)
|
99
|
+
end
|
100
|
+
|
101
|
+
def restored_callback
|
102
|
+
self.method(:scale_down)
|
103
|
+
end
|
104
|
+
|
105
|
+
protected def namespace
|
106
|
+
return "amigo/autoscaler/heroku"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Potentially add another worker to the formation.
|
110
|
+
# @return [:noscale, :maxscale, :scaled] One of :noscale (no +active_event_initial_workers+),
|
111
|
+
# :maxscale (+max_additional_workers+ reached), or :scaled.
|
112
|
+
def scale_up(_queues_and_latencies, depth:, **)
|
113
|
+
# When the scaling event starts (or if this is the first time we've seen it
|
114
|
+
# but the event is already in progress), store how many workers we have.
|
115
|
+
# It needs to be stored in redis so it persists if
|
116
|
+
# the latency event continues through restarts.
|
117
|
+
if @active_event_initial_workers.nil?
|
118
|
+
@active_event_initial_workers = @heroku.formation.info(@app_id_or_app_name, @formation_id_or_formation_type).
|
119
|
+
fetch("quantity")
|
120
|
+
Sidekiq.redis do |r|
|
121
|
+
r.set("#{namespace}/active_event_initial_workers", @active_event_initial_workers.to_s)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
return :noscale if @active_event_initial_workers.zero?
|
125
|
+
new_quantity = @active_event_initial_workers + depth
|
126
|
+
max_quantity = @active_event_initial_workers + @max_additional_workers
|
127
|
+
return :maxscale if new_quantity > max_quantity
|
128
|
+
@heroku.formation.update(@app_id_or_app_name, @formation_id_or_formation_type, {quantity: new_quantity})
|
129
|
+
return :scaled
|
130
|
+
end
|
131
|
+
|
132
|
+
# Reset the formation to +active_event_initial_workers+.
|
133
|
+
# @return [:noscale, :scaled] :noscale if +active_event_initial_workers+ is 0, otherwise :scaled.
|
134
|
+
def scale_down(**)
|
135
|
+
initial_workers = @active_event_initial_workers
|
136
|
+
Sidekiq.redis do |r|
|
137
|
+
r.del("#{namespace}/active_event_initial_workers")
|
138
|
+
end
|
139
|
+
@active_event_initial_workers = nil
|
140
|
+
return :noscale if initial_workers.zero?
|
141
|
+
@heroku.formation.update(@app_id_or_app_name, @formation_id_or_formation_type, {quantity: initial_workers})
|
142
|
+
return :scaled
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/amigo/autoscaler.rb
CHANGED
@@ -6,20 +6,31 @@ require "amigo"
|
|
6
6
|
|
7
7
|
# When queues achieve a latency that is too high,
|
8
8
|
# take some action.
|
9
|
-
# You should start this up at
|
9
|
+
# You should start this up at Web application startup:
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# # puma.rb or similar
|
12
|
+
# Amigo::Autoscaler.new.start
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# When latency grows beyond +latency_threshold+,
|
15
|
+
# a "high latency event" is started.
|
16
|
+
# Some action is taken, which is defined by the +handlers+ argument.
|
17
|
+
# This includes logging, alerting, and/or autoscaling.
|
16
18
|
#
|
17
|
-
#
|
19
|
+
# When latency returns to normal (defined by +latency_restored_threshold+),
|
20
|
+
# the high latency event finishes.
|
21
|
+
# Some additional action is taken, which is defined by the +latency_restored_handlers+ argument.
|
22
|
+
# Usually this is logging, and/or returning autoscaling to its original status.
|
18
23
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# 2) become more sophisticated with how we detect latency growth.
|
24
|
+
# There are several parameters to control behavior, such as how often polling is done,
|
25
|
+
# how often alerting/scaling is done, and more.
|
22
26
|
#
|
27
|
+
# As an example autoscaler that includes actual resource scaling,
|
28
|
+
# check out +Amigo::Autoscaler::Heroku+.
|
29
|
+
# Its ideas can easily be expanded to other platforms.
|
30
|
+
#
|
31
|
+
# Note that +Autoscaler+ maintains its state over multiple processes;
|
32
|
+
# it needs to keep track of high latency events even if the process running the autoscaler
|
33
|
+
# (usually a web process) restarts.
|
23
34
|
module Amigo
|
24
35
|
class Autoscaler
|
25
36
|
class InvalidHandler < StandardError; end
|
@@ -31,18 +42,26 @@ module Amigo
|
|
31
42
|
# @return [Integer]
|
32
43
|
attr_reader :latency_threshold
|
33
44
|
# What hosts/processes should this run on?
|
34
|
-
#
|
45
|
+
# Looks at ENV['DYNO'] and Socket.gethostname for a match.
|
35
46
|
# Default to only run on 'web.1', which is the first Heroku web dyno.
|
36
47
|
# We run on the web, not worker, dyno, so we report backed up queues
|
37
48
|
# in case we, say, turn off all workers (broken web processes
|
38
49
|
# are generally easier to find).
|
39
50
|
# @return [Regexp]
|
40
51
|
attr_reader :hostname_regex
|
41
|
-
# Methods to call when alerting.
|
42
|
-
# Valid values are 'log' and 'sentry' (requires Sentry to be required already).
|
43
|
-
# Anything that responds to +call+ will be invoked with
|
44
|
-
# `{queue name => latency in seconds}
|
45
|
-
#
|
52
|
+
# Methods to call when alerting, as strings/symbols or procs.
|
53
|
+
# Valid string values are 'log' and 'sentry' (requires Sentry to be required already).
|
54
|
+
# Anything that responds to +call+ will be invoked with:
|
55
|
+
# - Positional argument which is a +Hash+ of `{queue name => latency in seconds}`
|
56
|
+
# - Keyword argument +:depth+: Number of alerts as part of this latency event.
|
57
|
+
# For example, the first alert has a depth of 1, and if latency stays high,
|
58
|
+
# it'll be 2 on the next call, etc. +depth+ can be used to incrementally provision
|
59
|
+
# additional processing capacity, and stop adding capacity at a certain depth
|
60
|
+
# to avoid problems with too many workers (like excessive DB load).
|
61
|
+
# - Keyword argument +:duration+: Number of seconds since this latency spike started.
|
62
|
+
# - Additional undefined keywords. Handlers should accept additional options,
|
63
|
+
# like via `**kw` or `opts={}`, for compatibility.
|
64
|
+
# @return [Array<String,Symbol,Proc,#call>]
|
46
65
|
attr_reader :handlers
|
47
66
|
# Only alert this often.
|
48
67
|
# For example, with poll_interval of 10 seconds
|
@@ -50,20 +69,55 @@ module Amigo
|
|
50
69
|
# we'd alert once and then 210 seconds later.
|
51
70
|
# @return [Integer]
|
52
71
|
attr_reader :alert_interval
|
72
|
+
# After an alert happens, what latency should be considered "back to normal" and
|
73
|
+
# +latency_restored_handlers+ will be called?
|
74
|
+
# In most cases this should be the same as (and defaults to) +latency_threshold+
|
75
|
+
# so that we're 'back to normal' once we're below the threshold.
|
76
|
+
# It may also commonly be 0, so that the callback is fired when the queue is entirely clear.
|
77
|
+
# Note that, if +latency_restored_threshold+ is less than +latency_threshold+,
|
78
|
+
# while the latency is between the two, no alerts will fire.
|
79
|
+
attr_reader :latency_restored_threshold
|
80
|
+
# Methods to call when a latency of +latency_restored_threshold+ is reached
|
81
|
+
# (ie, when we get back to normal latency after a high latency event).
|
82
|
+
# Valid string values are 'log'.
|
83
|
+
# Usually this handler will deprovision capacity procured as part of the alert +handlers+.
|
84
|
+
# Anything that responds to +call+ will be invoked with:
|
85
|
+
# - Keyword +:depth+, the number of times an alert happened before
|
86
|
+
# the latency spike was resolved.
|
87
|
+
# - Keyword +:duration+, the number of seconds for the latency spike has been going on.
|
88
|
+
# - Additional undefined keywords. Handlers should accept additional options,
|
89
|
+
# like via `**kw`, for compatibility.
|
90
|
+
# @return [Array<String,Symbol,Proc,#call>]
|
91
|
+
attr_reader :latency_restored_handlers
|
92
|
+
# Proc/callable called with (level, message, params={}).
|
93
|
+
# By default, use +Amigo.log+ (which logs to the Sidekiq logger).
|
94
|
+
attr_reader :log
|
53
95
|
|
54
96
|
def initialize(
|
55
97
|
poll_interval: 20,
|
56
98
|
latency_threshold: 5,
|
57
99
|
hostname_regex: /^web\.1$/,
|
58
|
-
handlers: [
|
59
|
-
alert_interval: 120
|
100
|
+
handlers: [:log],
|
101
|
+
alert_interval: 120,
|
102
|
+
latency_restored_threshold: latency_threshold,
|
103
|
+
latency_restored_handlers: [:log],
|
104
|
+
log: ->(level, message, params={}) { Amigo.log(nil, level, message, params) }
|
60
105
|
)
|
61
106
|
|
107
|
+
raise ArgumentError, "latency_threshold must be > 0" if
|
108
|
+
latency_threshold <= 0
|
109
|
+
raise ArgumentError, "latency_restored_threshold must be >= 0" if
|
110
|
+
latency_restored_threshold.negative?
|
111
|
+
raise ArgumentError, "latency_restored_threshold must be <= latency_threshold" if
|
112
|
+
latency_restored_threshold > latency_threshold
|
62
113
|
@poll_interval = poll_interval
|
63
114
|
@latency_threshold = latency_threshold
|
64
115
|
@hostname_regex = hostname_regex
|
65
|
-
@handlers = handlers
|
116
|
+
@handlers = handlers.freeze
|
66
117
|
@alert_interval = alert_interval
|
118
|
+
@latency_restored_threshold = latency_restored_threshold
|
119
|
+
@latency_restored_handlers = latency_restored_handlers.freeze
|
120
|
+
@log = log
|
67
121
|
end
|
68
122
|
|
69
123
|
def polling_thread
|
@@ -73,17 +127,44 @@ module Amigo
|
|
73
127
|
def setup
|
74
128
|
# Store these as strings OR procs, rather than grabbing self.method here.
|
75
129
|
# It gets extremely hard ot test if we capture the method here.
|
76
|
-
@alert_methods = self.handlers.map
|
77
|
-
|
78
|
-
a
|
79
|
-
else
|
80
|
-
method_name = meth = "alert_#{a.strip}".to_sym
|
81
|
-
raise InvalidHandler, a.inspect unless self.method(method_name)
|
82
|
-
meth
|
83
|
-
end
|
84
|
-
end
|
85
|
-
@last_alerted = Time.at(0)
|
130
|
+
@alert_methods = self.handlers.map { |a| _handler_to_method("alert_", a) }
|
131
|
+
@restored_methods = self.latency_restored_handlers.map { |a| _handler_to_method("alert_restored_", a) }
|
86
132
|
@stop = false
|
133
|
+
Sidekiq.redis do |r|
|
134
|
+
@last_alerted = Time.at((r.get("#{namespace}/last_alerted") || 0).to_f)
|
135
|
+
@depth = (r.get("#{namespace}/depth") || 0).to_i
|
136
|
+
@latency_event_started = Time.at((r.get("#{namespace}/latency_event_started") || 0).to_f)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private def persist
|
141
|
+
Sidekiq.redis do |r|
|
142
|
+
r.set("#{namespace}/last_alerted", @last_alerted.to_f.to_s)
|
143
|
+
r.set("#{namespace}/depth", @depth.to_s)
|
144
|
+
r.set("#{namespace}/latency_event_started", @latency_event_started.to_f.to_s)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Delete all the keys that Autoscaler stores.
|
149
|
+
# Can be used in extreme cases where things need to be cleaned up,
|
150
|
+
# but should not be normally used.
|
151
|
+
def unpersist
|
152
|
+
Sidekiq.redis do |r|
|
153
|
+
r.del("#{namespace}/last_alerted")
|
154
|
+
r.del("#{namespace}/depth")
|
155
|
+
r.del("#{namespace}/latency_event_started")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
protected def namespace
|
160
|
+
return "amigo/autoscaler"
|
161
|
+
end
|
162
|
+
|
163
|
+
private def _handler_to_method(prefix, a)
|
164
|
+
return a if a.respond_to?(:call)
|
165
|
+
method_name = "#{prefix}#{a.to_s.strip}".to_sym
|
166
|
+
raise InvalidHandler, a.inspect unless (meth = self.method(method_name))
|
167
|
+
return meth
|
87
168
|
end
|
88
169
|
|
89
170
|
def start
|
@@ -92,7 +173,7 @@ module Amigo
|
|
92
173
|
hostname = ENV.fetch("DYNO") { Socket.gethostname }
|
93
174
|
return false unless self.hostname_regex.match?(hostname)
|
94
175
|
|
95
|
-
self.
|
176
|
+
self._log(:info, "async_autoscaler_starting")
|
96
177
|
self.setup
|
97
178
|
@polling_thread = Thread.new do
|
98
179
|
until @stop
|
@@ -109,21 +190,53 @@ module Amigo
|
|
109
190
|
|
110
191
|
def check
|
111
192
|
now = Time.now
|
112
|
-
skip_check = now < (@last_alerted + self.
|
193
|
+
skip_check = now < (@last_alerted + self.alert_interval)
|
113
194
|
if skip_check
|
114
|
-
self.
|
195
|
+
self._log(:debug, "async_autoscaler_skip_check")
|
115
196
|
return
|
116
197
|
end
|
117
|
-
self.
|
198
|
+
self._log(:info, "async_autoscaler_check")
|
118
199
|
high_latency_queues = Sidekiq::Queue.all.
|
119
200
|
map { |q| [q.name, q.latency] }.
|
120
201
|
select { |(_, latency)| latency > self.latency_threshold }.
|
121
202
|
to_h
|
122
|
-
|
203
|
+
if high_latency_queues.empty?
|
204
|
+
# Whenever we are in a latency event, we have a depth > 0. So a depth of 0 means
|
205
|
+
# we're not in a latency event, and still have no latency, so can noop.
|
206
|
+
return if @depth.zero?
|
207
|
+
# We WERE in a latency event, and now we're not, so report on it.
|
208
|
+
@restored_methods.each do |m|
|
209
|
+
m.call(depth: @depth, duration: (Time.now - @latency_event_started).to_f)
|
210
|
+
end
|
211
|
+
# Reset back to 0 depth so we know we're not in a latency event.
|
212
|
+
@depth = 0
|
213
|
+
@latency_event_started = Time.at(0)
|
214
|
+
@last_alerted = now
|
215
|
+
self.persist
|
216
|
+
return
|
217
|
+
end
|
218
|
+
if @depth.positive?
|
219
|
+
# We have already alerted, so increment the depth and when the latency started.
|
220
|
+
@depth += 1
|
221
|
+
duration = (Time.now - @latency_event_started).to_f
|
222
|
+
else
|
223
|
+
# Indicate we are starting a high latency event.
|
224
|
+
@depth = 1
|
225
|
+
@latency_event_started = Time.now
|
226
|
+
duration = 0.0
|
227
|
+
end
|
228
|
+
# Alert each handler. For legacy reasons, we support handlers that accept
|
229
|
+
# ({queues and latencies}) and ({queues and latencies}, {}keywords}).
|
230
|
+
kw = {depth: @depth, duration: duration}
|
123
231
|
@alert_methods.each do |m|
|
124
|
-
m.respond_to?(:
|
232
|
+
if m.respond_to?(:arity) && m.arity == 1
|
233
|
+
m.call(high_latency_queues)
|
234
|
+
else
|
235
|
+
m.call(high_latency_queues, **kw)
|
236
|
+
end
|
125
237
|
end
|
126
238
|
@last_alerted = now
|
239
|
+
self.persist
|
127
240
|
end
|
128
241
|
|
129
242
|
def alert_sentry(names_and_latencies)
|
@@ -134,14 +247,18 @@ module Amigo
|
|
134
247
|
end
|
135
248
|
end
|
136
249
|
|
137
|
-
def alert_log(names_and_latencies)
|
138
|
-
self.
|
250
|
+
def alert_log(names_and_latencies, depth:, duration:)
|
251
|
+
self._log(:warn, "high_latency_queues", queues: names_and_latencies, depth: depth, duration: duration)
|
139
252
|
end
|
140
253
|
|
141
|
-
def alert_test(_names_and_latencies); end
|
254
|
+
def alert_test(_names_and_latencies, _opts={}); end
|
255
|
+
|
256
|
+
def alert_restored_log(depth:, duration:)
|
257
|
+
self._log(:info, "high_latency_queues_restored", depth: depth, duration: duration)
|
258
|
+
end
|
142
259
|
|
143
|
-
protected def
|
144
|
-
|
260
|
+
protected def _log(level, msg, **kw)
|
261
|
+
self.log[level, msg, kw]
|
145
262
|
end
|
146
263
|
end
|
147
264
|
end
|
data/lib/amigo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-amigo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lithic Technology
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: platform-api
|
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'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rack
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,28 +100,28 @@ dependencies:
|
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: '1.
|
103
|
+
version: '1.48'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: '1.
|
110
|
+
version: '1.48'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rubocop-performance
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: '1.
|
117
|
+
version: '1.16'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '1.
|
124
|
+
version: '1.16'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: sentry-ruby
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,10 +150,24 @@ dependencies:
|
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: webmock
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
139
167
|
description: 'sidekiq-amigo provides a pubsub system and other enhancements around
|
140
168
|
Sidekiq.
|
141
169
|
|
142
|
-
'
|
170
|
+
'
|
143
171
|
email: hello@lithic.tech
|
144
172
|
executables: []
|
145
173
|
extensions: []
|
@@ -148,6 +176,7 @@ files:
|
|
148
176
|
- lib/amigo.rb
|
149
177
|
- lib/amigo/audit_logger.rb
|
150
178
|
- lib/amigo/autoscaler.rb
|
179
|
+
- lib/amigo/autoscaler/heroku.rb
|
151
180
|
- lib/amigo/deprecated_jobs.rb
|
152
181
|
- lib/amigo/job.rb
|
153
182
|
- lib/amigo/queue_backoff_job.rb
|
@@ -163,7 +192,7 @@ licenses:
|
|
163
192
|
- MIT
|
164
193
|
metadata:
|
165
194
|
rubygems_mfa_required: 'true'
|
166
|
-
post_install_message:
|
195
|
+
post_install_message:
|
167
196
|
rdoc_options: []
|
168
197
|
require_paths:
|
169
198
|
- lib
|
@@ -178,8 +207,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
207
|
- !ruby/object:Gem::Version
|
179
208
|
version: '0'
|
180
209
|
requirements: []
|
181
|
-
rubygems_version: 3.
|
182
|
-
signing_key:
|
210
|
+
rubygems_version: 3.3.7
|
211
|
+
signing_key:
|
183
212
|
specification_version: 4
|
184
213
|
summary: Pubsub system and other enhancements around Sidekiq.
|
185
214
|
test_files: []
|