sidekiq-amigo 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|