cutoff 0.4.1 → 0.4.2
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/CHANGELOG.md +7 -1
- data/README.md +43 -0
- data/lib/cutoff/patch/mysql2.rb +4 -3
- data/lib/cutoff/patch/net_http.rb +5 -2
- data/lib/cutoff/rails.rb +0 -4
- data/lib/cutoff/sidekiq.rb +40 -0
- data/lib/cutoff/version.rb +1 -1
- data/lib/cutoff.rb +52 -14
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d00ff1f831beec4fed582b5faa9ed42c350c13b32217bdcab0b850445ba9c358
|
4
|
+
data.tar.gz: 77d466db54a822240d2d27cfdb120f98aa7f7435ec62ae15191292702580de79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9821d7010a4663ce25497a129c5a04166b5f701bd30d1d7a20f183cd274444605f3ac14099857c22117d45ac980056f4ccbaf09e8b65403e83a93abc923ced90
|
7
|
+
data.tar.gz: ec1f98a8885f8938ba421d87cb82f7f16a79161a778d2e4c230204f2243009b001e4c5de8c95ca1153746a8eebb90f969ca5efe80390cd08f901ee943a06581f
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.4.2] - 2021-10-14
|
11
|
+
|
12
|
+
- Add sidekiq middleware
|
13
|
+
- Select checkpoints to enable or disable
|
14
|
+
|
10
15
|
## [0.4.1] - 2021-10-02
|
11
16
|
|
12
17
|
- Fix Net::HTTP patch to override timeouts given to start
|
@@ -34,7 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
34
39
|
- Mysql2 patch
|
35
40
|
|
36
41
|
[Unreleased]: https://github.com/justinhoward/cutoff/compare/v0.4.1...HEAD
|
37
|
-
[0.4.
|
42
|
+
[0.4.2]: https://github.com/justinhoward/cutoff/compare/v0.4.1...v0.4.2
|
43
|
+
[0.4.1]: https://github.com/justinhoward/cutoff/compare/v0.4.0...v0.4.1
|
38
44
|
[0.4.0]: https://github.com/justinhoward/cutoff/compare/v0.3.0...v0.4.0
|
39
45
|
[0.3.0]: https://github.com/justinhoward/cutoff/compare/v0.2.0...v0.3.0
|
40
46
|
[0.2.0]: https://github.com/justinhoward/cutoff/compare/v0.1.0...v0.2.0
|
data/README.md
CHANGED
@@ -217,6 +217,29 @@ Cutoff.wrap(3) do
|
|
217
217
|
end
|
218
218
|
```
|
219
219
|
|
220
|
+
Selecting Checkpoints
|
221
|
+
---------------------------
|
222
|
+
|
223
|
+
In some cases, you may want to select some checkpoints to use, but not others.
|
224
|
+
For example, you may want to run some code that contains MySQL queries, but not
|
225
|
+
use the mysql2 patch. The `exclude` and `only` options support this.
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
Cutoff.wrap(10, exclude: :mysql2) do
|
229
|
+
# The mysql2 patch won't be used here
|
230
|
+
end
|
231
|
+
|
232
|
+
Cutoff.wrap(10, only: %i[foo bar]) do
|
233
|
+
# These checkpoints will be used
|
234
|
+
Cutoff.checkpoint!(:foo)
|
235
|
+
Cutoff.checkpoint!(:bar)
|
236
|
+
|
237
|
+
# These checkpoints will be skipped
|
238
|
+
Cutoff.checkpoint!(:asdf)
|
239
|
+
Cutoff.checkpoint!
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
220
243
|
Timing a Rails Controller
|
221
244
|
---------------------------
|
222
245
|
|
@@ -277,6 +300,26 @@ class ApplicationController < ActionController::Base
|
|
277
300
|
end
|
278
301
|
```
|
279
302
|
|
303
|
+
Timing Sidekiq Workers
|
304
|
+
------------
|
305
|
+
|
306
|
+
If Sidekiq is loaded, Cutoff includes middleware to support a `:cutoff` option.
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
class MyWorker
|
310
|
+
include Sidekiq::Worker
|
311
|
+
|
312
|
+
sidekiq_options cutoff: 6.0
|
313
|
+
|
314
|
+
def perform
|
315
|
+
# ...
|
316
|
+
Cutoff.checkpoint!
|
317
|
+
# ...
|
318
|
+
end
|
319
|
+
end
|
320
|
+
```
|
321
|
+
|
322
|
+
|
280
323
|
Disabling Cutoff for Testing and Development
|
281
324
|
------------
|
282
325
|
|
data/lib/cutoff/patch/mysql2.rb
CHANGED
@@ -6,7 +6,8 @@ require 'mysql2'
|
|
6
6
|
class Cutoff
|
7
7
|
module Patch
|
8
8
|
# Sets the max execution time for SELECT queries if there is an active
|
9
|
-
# cutoff and it has time remaining
|
9
|
+
# cutoff and it has time remaining. You can select this patch with
|
10
|
+
# `exclude` or `only` using the checkpoint name `:mysql2`.
|
10
11
|
module Mysql2
|
11
12
|
# Overrides `Mysql2::Client#query` to insert a MAX_EXECUTION_TIME query
|
12
13
|
# hint with the remaining cutoff time
|
@@ -19,9 +20,9 @@ class Cutoff
|
|
19
20
|
# be executed in this case.
|
20
21
|
def query(sql, options = {})
|
21
22
|
cutoff = Cutoff.current
|
22
|
-
return super unless cutoff
|
23
|
+
return super unless cutoff&.selected?(:mysql2)
|
23
24
|
|
24
|
-
cutoff.checkpoint!
|
25
|
+
cutoff.checkpoint!(:mysql2)
|
25
26
|
sql = QueryWithMaxTime.new(sql, cutoff.ms_remaining.ceil).to_s
|
26
27
|
super
|
27
28
|
end
|
@@ -4,6 +4,9 @@ require 'net/http'
|
|
4
4
|
|
5
5
|
class Cutoff
|
6
6
|
module Patch
|
7
|
+
# Set checkpoints for Ruby HTTP requests. Also sets the Net::HTTP timeouts
|
8
|
+
# to the remaining cutoff time. You can select this patch with
|
9
|
+
# `exclude` or `only` using the checkpoint name `:net_http`.
|
7
10
|
module NetHttp
|
8
11
|
def self.gen_timeout_method(name)
|
9
12
|
<<~RUBY
|
@@ -23,13 +26,13 @@ class Cutoff
|
|
23
26
|
# @see Net::HTTP#start
|
24
27
|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
25
28
|
def start
|
26
|
-
if (cutoff = Cutoff.current)
|
29
|
+
if (cutoff = Cutoff.current) && cutoff.selected?(:net_http)
|
27
30
|
remaining = cutoff.seconds_remaining
|
28
31
|
#{gen_timeout_method('open_timeout')}
|
29
32
|
#{gen_timeout_method('read_timeout')}
|
30
33
|
#{gen_timeout_method('write_timeout') if use_write_timeout?}
|
31
34
|
#{gen_timeout_method('continue_timeout')}
|
32
|
-
Cutoff.checkpoint!
|
35
|
+
Cutoff.checkpoint!(:net_http)
|
33
36
|
end
|
34
37
|
super
|
35
38
|
end
|
data/lib/cutoff/rails.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sidekiq'
|
4
|
+
|
5
|
+
class Cutoff
|
6
|
+
module Sidekiq
|
7
|
+
# Add an option `cutoff` for sidekiq workers
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class MyWorker
|
11
|
+
# include Sidekiq::Worker
|
12
|
+
#
|
13
|
+
# sidekiq_options cutoff: 6.0
|
14
|
+
#
|
15
|
+
# def perform
|
16
|
+
# # ...
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
class ServerMiddleware
|
20
|
+
# @param worker [Object] the worker instance
|
21
|
+
# @param _job [Hash] the full job payload
|
22
|
+
# @param _queue [String] queue the name of the queue the job was pulled
|
23
|
+
# from
|
24
|
+
# @yield the next middleware in the chain or worker `perform` method
|
25
|
+
# @return [void]
|
26
|
+
def call(worker, _job, _queue)
|
27
|
+
allowed_seconds = worker.class.sidekiq_options['cutoff']
|
28
|
+
return yield if allowed_seconds.nil?
|
29
|
+
|
30
|
+
Cutoff.wrap(allowed_seconds) { yield }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
::Sidekiq.configure_server do |config|
|
37
|
+
config.server_middleware do |chain|
|
38
|
+
chain.add(Cutoff::Sidekiq::ServerMiddleware)
|
39
|
+
end
|
40
|
+
end
|
data/lib/cutoff/version.rb
CHANGED
data/lib/cutoff.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal:true
|
2
2
|
|
3
|
+
require 'set'
|
4
|
+
|
3
5
|
require 'cutoff/version'
|
4
6
|
require 'cutoff/error'
|
5
7
|
require 'cutoff/patch'
|
6
8
|
require 'cutoff/timer'
|
9
|
+
|
7
10
|
require 'cutoff/rails'
|
11
|
+
require 'cutoff/sidekiq' if Gem.loaded_specs['sidekiq']
|
8
12
|
|
9
13
|
class Cutoff
|
10
14
|
CURRENT_STACK_KEY = 'cutoff_deadline_stack'
|
@@ -25,13 +29,14 @@ class Cutoff
|
|
25
29
|
# If a cutoff is already started for this thread, then `start` uses the
|
26
30
|
# minimum of the current remaining time and the given time
|
27
31
|
#
|
28
|
-
# @param
|
29
|
-
# be overridden if there is an active cutoff and it has less remaining
|
30
|
-
# time.
|
32
|
+
# @param (see #initialize)
|
31
33
|
# @return [Cutoff] The {Cutoff} instance
|
32
|
-
def start(
|
33
|
-
|
34
|
-
|
34
|
+
def start(allowed_seconds, **options)
|
35
|
+
if current
|
36
|
+
allowed_seconds = [allowed_seconds, current.seconds_remaining].min
|
37
|
+
end
|
38
|
+
|
39
|
+
cutoff = new(allowed_seconds, **options)
|
35
40
|
Thread.current[CURRENT_STACK_KEY] ||= []
|
36
41
|
Thread.current[CURRENT_STACK_KEY] << cutoff
|
37
42
|
cutoff
|
@@ -68,9 +73,10 @@ class Cutoff
|
|
68
73
|
#
|
69
74
|
# @see .start
|
70
75
|
# @see .stop
|
76
|
+
# @param (see #initialize)
|
71
77
|
# @return The value that returned from the block
|
72
|
-
def wrap(
|
73
|
-
cutoff = start(
|
78
|
+
def wrap(allowed_seconds, **options)
|
79
|
+
cutoff = start(allowed_seconds, **options)
|
74
80
|
yield cutoff
|
75
81
|
ensure
|
76
82
|
stop(cutoff)
|
@@ -82,11 +88,11 @@ class Cutoff
|
|
82
88
|
#
|
83
89
|
# @raise CutoffExceededError If there is an active expired cutoff
|
84
90
|
# @return [void]
|
85
|
-
def checkpoint!
|
91
|
+
def checkpoint!(name = nil)
|
86
92
|
cutoff = current
|
87
93
|
return unless cutoff
|
88
94
|
|
89
|
-
cutoff.checkpoint!
|
95
|
+
cutoff.checkpoint!(name)
|
90
96
|
end
|
91
97
|
|
92
98
|
# Disable Cutoff globally. Useful for testing and debugging
|
@@ -107,7 +113,7 @@ class Cutoff
|
|
107
113
|
@disabled = false
|
108
114
|
end
|
109
115
|
|
110
|
-
# True if cutoff was disabled with {
|
116
|
+
# True if cutoff was disabled with {.disable!}
|
111
117
|
#
|
112
118
|
# @return [Boolean] True if disabled
|
113
119
|
def disabled?
|
@@ -122,10 +128,22 @@ class Cutoff
|
|
122
128
|
#
|
123
129
|
# The timer starts immediately upon creation
|
124
130
|
#
|
125
|
-
# @param allowed_seconds [
|
126
|
-
|
131
|
+
# @param allowed_seconds [Float, Integer] The total number of seconds to allow
|
132
|
+
# @param exclude [Enumberable<Symbol>, Symbol, nil] If given a name or
|
133
|
+
# list of checkpoint names to skip
|
134
|
+
# @param only [Enumberable<Symbol>, Symbol, nil] If given a name or
|
135
|
+
# list of checkpoint names to allow
|
136
|
+
def initialize(allowed_seconds, exclude: nil, only: nil)
|
127
137
|
@allowed_seconds = allowed_seconds.to_f
|
128
138
|
@start_time = Cutoff.now
|
139
|
+
|
140
|
+
if exclude
|
141
|
+
@exclude = Set.new(exclude.is_a?(Enumerable) ? exclude : [exclude])
|
142
|
+
end
|
143
|
+
|
144
|
+
if only
|
145
|
+
@only = Set.new(only.is_a?(Enumerable) ? only : [only])
|
146
|
+
end
|
129
147
|
end
|
130
148
|
|
131
149
|
# The number of seconds left on the clock
|
@@ -162,9 +180,29 @@ class Cutoff
|
|
162
180
|
#
|
163
181
|
# @raise CutoffExceededError If there is an active expired cutoff
|
164
182
|
# @return [void]
|
165
|
-
def checkpoint!
|
183
|
+
def checkpoint!(name = nil)
|
184
|
+
unless name.nil? || name.is_a?(Symbol)
|
185
|
+
raise ArgumentError, 'name must be a symbol'
|
186
|
+
end
|
187
|
+
|
188
|
+
return unless selected?(name)
|
166
189
|
raise CutoffExceededError, self if exceeded?
|
167
190
|
|
168
191
|
nil
|
169
192
|
end
|
193
|
+
|
194
|
+
# True if the named checkpoint is selected by the `exclude` and `only`
|
195
|
+
# options. If these options are not given, a checkpoint is considered to be
|
196
|
+
# selected. If the checkpoint is not named, it is also considered to be
|
197
|
+
# selected.
|
198
|
+
#
|
199
|
+
# @param name [Symbol, nil] The name of the checkpoint
|
200
|
+
# @return [Boolean] True if the checkpoint is selected
|
201
|
+
def selected?(name)
|
202
|
+
return true if name.nil? && @exclude
|
203
|
+
return false if @exclude&.include?(name)
|
204
|
+
return false if @only && !@only.include?(name)
|
205
|
+
|
206
|
+
true
|
207
|
+
end
|
170
208
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cutoff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Howard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- lib/cutoff/patch/net_http.rb
|
105
105
|
- lib/cutoff/rails.rb
|
106
106
|
- lib/cutoff/rails/controller.rb
|
107
|
+
- lib/cutoff/sidekiq.rb
|
107
108
|
- lib/cutoff/timer.rb
|
108
109
|
- lib/cutoff/version.rb
|
109
110
|
homepage: https://github.com/justinhoward/cutoff
|
@@ -111,7 +112,7 @@ licenses:
|
|
111
112
|
- MIT
|
112
113
|
metadata:
|
113
114
|
changelog_uri: https://github.com/justinhoward/cutoff/blob/master/CHANGELOG.md
|
114
|
-
documentation_uri: https://www.rubydoc.info/gems/cutoff/0.4.
|
115
|
+
documentation_uri: https://www.rubydoc.info/gems/cutoff/0.4.2
|
115
116
|
post_install_message:
|
116
117
|
rdoc_options: []
|
117
118
|
require_paths:
|