cutoff 0.4.0 → 0.5.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/CHANGELOG.md +33 -1
 - data/README.md +43 -0
 - data/lib/cutoff/error.rb +4 -2
 - data/lib/cutoff/patch/mysql2.rb +4 -3
 - data/lib/cutoff/patch/net_http.rb +28 -16
 - data/lib/cutoff/rails.rb +0 -4
 - data/lib/cutoff/sidekiq.rb +40 -0
 - data/lib/cutoff/timer.rb +8 -9
 - data/lib/cutoff/version.rb +1 -1
 - data/lib/cutoff.rb +53 -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: d13746c455ac05f8491ccd8e5f8834370c0e4d41b77b440a2ee33f7c9bafb9db
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 883490b2c6661605d81cf299f809175cadfd1e51b4c68bb218dfa8f996af23cc
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: e544dff1eb63defd93d8f1f260c12e843bb3178a1a523d63b327ea76ddea533bcd6ce7c68c2b697d859ad795ad62f8048549ae82997c0d6c9edbee92089de6c0
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a1612206aaf0400aa50988b32b35fd7e27418d047e5aab6d7919dfedd30efeaa3675e97f9b6134233f13a54794c01ff7d8868ac892ebf711fc42ef2a65388de5
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -7,13 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            ## [Unreleased]
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
            ## [0.5.0] - 2022-08-10
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            - Use CLOCK_MONOTONIC instead of CLOCK_MONOTONIC_RAW #10 justinhoward
         
     | 
| 
      
 15 
     | 
    
         
            +
            - Change CutoffExceededError to inherit from Timeout::Error #9 justinhoward
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            ### Breaking
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            PR #9 changes the parent class of `Cutoff::CutoffExceededError` from `CutoffError`
         
     | 
| 
      
 20 
     | 
    
         
            +
            to `Timeout::Error`. `CutoffError` changes from a class to a module.
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ## [0.4.2] - 2021-10-14
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            - Add sidekiq middleware
         
     | 
| 
      
 27 
     | 
    
         
            +
            - Select checkpoints to enable or disable
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            ## [0.4.1] - 2021-10-02
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            ### Fixed
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            - Fix Net::HTTP patch to override timeouts given to start
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       10 
35 
     | 
    
         
             
            ## [0.4.0] - 2021-10-01
         
     | 
| 
       11 
36 
     | 
    
         | 
| 
      
 37 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       12 
39 
     | 
    
         
             
            - Add benchmarks and slight performance improvements
         
     | 
| 
       13 
40 
     | 
    
         
             
            - Add Rails controller integration
         
     | 
| 
       14 
41 
     | 
    
         | 
| 
       15 
42 
     | 
    
         
             
            ## [0.3.0] - 2021-08-20
         
     | 
| 
       16 
43 
     | 
    
         | 
| 
      
 44 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       17 
46 
     | 
    
         
             
            - Allow timers to be disabled globally with `Cutoff.disable!`
         
     | 
| 
       18 
47 
     | 
    
         | 
| 
       19 
48 
     | 
    
         
             
            ## [0.2.0] - 2021-07-22
         
     | 
| 
         @@ -29,7 +58,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       29 
58 
     | 
    
         
             
            - Cutoff class
         
     | 
| 
       30 
59 
     | 
    
         
             
            - Mysql2 patch
         
     | 
| 
       31 
60 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            [Unreleased]: https://github.com/justinhoward/cutoff/compare/v0. 
     | 
| 
      
 61 
     | 
    
         
            +
            [Unreleased]: https://github.com/justinhoward/cutoff/compare/v0.5.0...HEAD
         
     | 
| 
      
 62 
     | 
    
         
            +
            [0.5.0]: https://github.com/justinhoward/cutoff/compare/v0.4.2...v0.5.0
         
     | 
| 
      
 63 
     | 
    
         
            +
            [0.4.2]: https://github.com/justinhoward/cutoff/compare/v0.4.1...v0.4.2
         
     | 
| 
      
 64 
     | 
    
         
            +
            [0.4.1]: https://github.com/justinhoward/cutoff/compare/v0.4.0...v0.4.1
         
     | 
| 
       33 
65 
     | 
    
         
             
            [0.4.0]: https://github.com/justinhoward/cutoff/compare/v0.3.0...v0.4.0
         
     | 
| 
       34 
66 
     | 
    
         
             
            [0.3.0]: https://github.com/justinhoward/cutoff/compare/v0.2.0...v0.3.0
         
     | 
| 
       35 
67 
     | 
    
         
             
            [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/error.rb
    CHANGED
    
    | 
         @@ -2,7 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            class Cutoff
         
     | 
| 
       4 
4 
     | 
    
         
             
              # The Cutoff base error class
         
     | 
| 
       5 
     | 
    
         
            -
               
     | 
| 
      
 5 
     | 
    
         
            +
              module CutoffError
         
     | 
| 
       6 
6 
     | 
    
         
             
                private
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                def message_with_meta(message, **meta)
         
     | 
| 
         @@ -15,7 +15,9 @@ class Cutoff 
     | 
|
| 
       15 
15 
     | 
    
         
             
              end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
              # Raised by {Cutoff#checkpoint!} if the time has been exceeded
         
     | 
| 
       18 
     | 
    
         
            -
              class CutoffExceededError <  
     | 
| 
      
 18 
     | 
    
         
            +
              class CutoffExceededError < Timeout::Error
         
     | 
| 
      
 19 
     | 
    
         
            +
                include CutoffError
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
       19 
21 
     | 
    
         
             
                attr_reader :cutoff
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
23 
     | 
    
         
             
                def initialize(cutoff)
         
     | 
    
        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,27 +4,39 @@ require 'net/http' 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            class Cutoff
         
     | 
| 
       6 
6 
     | 
    
         
             
              module Patch
         
     | 
| 
       7 
     | 
    
         
            -
                #  
     | 
| 
       8 
     | 
    
         
            -
                # to the remaining time
         
     | 
| 
      
 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`.
         
     | 
| 
       9 
10 
     | 
    
         
             
                module NetHttp
         
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def self.gen_timeout_method(name)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    <<~RUBY
         
     | 
| 
      
 13 
     | 
    
         
            +
                      if #{name}.nil? || #{name} > remaining
         
     | 
| 
      
 14 
     | 
    
         
            +
                        self.#{name} = cutoff.seconds_remaining
         
     | 
| 
      
 15 
     | 
    
         
            +
                      end
         
     | 
| 
      
 16 
     | 
    
         
            +
                    RUBY
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
       15 
18 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
                    @write_timeout = cutoff.seconds_remaining
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.use_write_timeout?
         
     | 
| 
      
 20 
     | 
    
         
            +
                    Gem::Version.new(RUBY_VERSION) > Gem::Version.new('2.6')
         
     | 
| 
       19 
21 
     | 
    
         
             
                  end
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                  # Same as the original start, but  
     | 
| 
      
 23 
     | 
    
         
            +
                  # Same as the original start, but adds a checkpoint for starting HTTP
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # requests and sets network timeouts to the remaining time
         
     | 
| 
       22 
25 
     | 
    
         
             
                  #
         
     | 
| 
       23 
     | 
    
         
            -
                  # @see  
     | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
                     
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # @see Net::HTTP#start
         
     | 
| 
      
 27 
     | 
    
         
            +
                  module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    def start
         
     | 
| 
      
 29 
     | 
    
         
            +
                      if (cutoff = Cutoff.current) && cutoff.selected?(:net_http)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        remaining = cutoff.seconds_remaining
         
     | 
| 
      
 31 
     | 
    
         
            +
                        #{gen_timeout_method('open_timeout')}
         
     | 
| 
      
 32 
     | 
    
         
            +
                        #{gen_timeout_method('read_timeout')}
         
     | 
| 
      
 33 
     | 
    
         
            +
                        #{gen_timeout_method('write_timeout') if use_write_timeout?}
         
     | 
| 
      
 34 
     | 
    
         
            +
                        #{gen_timeout_method('continue_timeout')}
         
     | 
| 
      
 35 
     | 
    
         
            +
                        Cutoff.checkpoint!(:net_http)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                      super
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  RUBY
         
     | 
| 
       28 
40 
     | 
    
         
             
                end
         
     | 
| 
       29 
41 
     | 
    
         
             
              end
         
     | 
| 
       30 
42 
     | 
    
         
             
            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/timer.rb
    CHANGED
    
    | 
         @@ -2,18 +2,17 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            class Cutoff
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Timer
         
     | 
| 
       5 
     | 
    
         
            -
                if defined?(Process:: 
     | 
| 
       6 
     | 
    
         
            -
                  # The current time
         
     | 
| 
      
 5 
     | 
    
         
            +
                if defined?(Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # The current relative time
         
     | 
| 
       7 
7 
     | 
    
         
             
                  #
         
     | 
| 
       8 
8 
     | 
    
         
             
                  # If it is available, this will use a monotonic clock. This is a clock
         
     | 
| 
       9 
     | 
    
         
            -
                  # that always moves forward in time 
     | 
| 
       10 
     | 
    
         
            -
                  # system 
     | 
| 
      
 9 
     | 
    
         
            +
                  # that always moves forward in time and starts at an arbitrary point
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # (such as system startup time). If that is not available on this system,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # `Time.now` will be used.
         
     | 
| 
       11 
12 
     | 
    
         
             
                  #
         
     | 
| 
       12 
     | 
    
         
            -
                  #  
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                  end
         
     | 
| 
       16 
     | 
    
         
            -
                elsif defined?(Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # This does not represent current real time
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @return [Float] The current relative time as a float
         
     | 
| 
       17 
16 
     | 
    
         
             
                  def now
         
     | 
| 
       18 
17 
     | 
    
         
             
                    Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
       19 
18 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/cutoff/version.rb
    CHANGED
    
    
    
        data/lib/cutoff.rb
    CHANGED
    
    | 
         @@ -1,10 +1,15 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal:true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'timeout'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       3 
6 
     | 
    
         
             
            require 'cutoff/version'
         
     | 
| 
       4 
7 
     | 
    
         
             
            require 'cutoff/error'
         
     | 
| 
       5 
8 
     | 
    
         
             
            require 'cutoff/patch'
         
     | 
| 
       6 
9 
     | 
    
         
             
            require 'cutoff/timer'
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       7 
11 
     | 
    
         
             
            require 'cutoff/rails'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'cutoff/sidekiq' if Gem.loaded_specs['sidekiq']
         
     | 
| 
       8 
13 
     | 
    
         | 
| 
       9 
14 
     | 
    
         
             
            class Cutoff
         
     | 
| 
       10 
15 
     | 
    
         
             
              CURRENT_STACK_KEY = 'cutoff_deadline_stack'
         
     | 
| 
         @@ -25,13 +30,14 @@ class Cutoff 
     | 
|
| 
       25 
30 
     | 
    
         
             
                # If a cutoff is already started for this thread, then `start` uses the
         
     | 
| 
       26 
31 
     | 
    
         
             
                # minimum of the current remaining time and the given time
         
     | 
| 
       27 
32 
     | 
    
         
             
                #
         
     | 
| 
       28 
     | 
    
         
            -
                # @param  
     | 
| 
       29 
     | 
    
         
            -
                #   be overridden if there is an active cutoff and it has less remaining
         
     | 
| 
       30 
     | 
    
         
            -
                #   time.
         
     | 
| 
      
 33 
     | 
    
         
            +
                # @param (see #initialize)
         
     | 
| 
       31 
34 
     | 
    
         
             
                # @return [Cutoff] The {Cutoff} instance
         
     | 
| 
       32 
     | 
    
         
            -
                def start( 
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
                def start(allowed_seconds, **options)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  if current
         
     | 
| 
      
 37 
     | 
    
         
            +
                    allowed_seconds = [allowed_seconds, current.seconds_remaining].min
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  cutoff = new(allowed_seconds, **options)
         
     | 
| 
       35 
41 
     | 
    
         
             
                  Thread.current[CURRENT_STACK_KEY] ||= []
         
     | 
| 
       36 
42 
     | 
    
         
             
                  Thread.current[CURRENT_STACK_KEY] << cutoff
         
     | 
| 
       37 
43 
     | 
    
         
             
                  cutoff
         
     | 
| 
         @@ -68,9 +74,10 @@ class Cutoff 
     | 
|
| 
       68 
74 
     | 
    
         
             
                #
         
     | 
| 
       69 
75 
     | 
    
         
             
                # @see .start
         
     | 
| 
       70 
76 
     | 
    
         
             
                # @see .stop
         
     | 
| 
      
 77 
     | 
    
         
            +
                # @param (see #initialize)
         
     | 
| 
       71 
78 
     | 
    
         
             
                # @return The value that returned from the block
         
     | 
| 
       72 
     | 
    
         
            -
                def wrap( 
     | 
| 
       73 
     | 
    
         
            -
                  cutoff = start( 
     | 
| 
      
 79 
     | 
    
         
            +
                def wrap(allowed_seconds, **options)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  cutoff = start(allowed_seconds, **options)
         
     | 
| 
       74 
81 
     | 
    
         
             
                  yield cutoff
         
     | 
| 
       75 
82 
     | 
    
         
             
                ensure
         
     | 
| 
       76 
83 
     | 
    
         
             
                  stop(cutoff)
         
     | 
| 
         @@ -82,11 +89,11 @@ class Cutoff 
     | 
|
| 
       82 
89 
     | 
    
         
             
                #
         
     | 
| 
       83 
90 
     | 
    
         
             
                # @raise CutoffExceededError If there is an active expired cutoff
         
     | 
| 
       84 
91 
     | 
    
         
             
                # @return [void]
         
     | 
| 
       85 
     | 
    
         
            -
                def checkpoint!
         
     | 
| 
      
 92 
     | 
    
         
            +
                def checkpoint!(name = nil)
         
     | 
| 
       86 
93 
     | 
    
         
             
                  cutoff = current
         
     | 
| 
       87 
94 
     | 
    
         
             
                  return unless cutoff
         
     | 
| 
       88 
95 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                  cutoff.checkpoint!
         
     | 
| 
      
 96 
     | 
    
         
            +
                  cutoff.checkpoint!(name)
         
     | 
| 
       90 
97 
     | 
    
         
             
                end
         
     | 
| 
       91 
98 
     | 
    
         | 
| 
       92 
99 
     | 
    
         
             
                # Disable Cutoff globally. Useful for testing and debugging
         
     | 
| 
         @@ -107,7 +114,7 @@ class Cutoff 
     | 
|
| 
       107 
114 
     | 
    
         
             
                  @disabled = false
         
     | 
| 
       108 
115 
     | 
    
         
             
                end
         
     | 
| 
       109 
116 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
                # True if cutoff was disabled with { 
     | 
| 
      
 117 
     | 
    
         
            +
                # True if cutoff was disabled with {.disable!}
         
     | 
| 
       111 
118 
     | 
    
         
             
                #
         
     | 
| 
       112 
119 
     | 
    
         
             
                # @return [Boolean] True if disabled
         
     | 
| 
       113 
120 
     | 
    
         
             
                def disabled?
         
     | 
| 
         @@ -122,10 +129,22 @@ class Cutoff 
     | 
|
| 
       122 
129 
     | 
    
         
             
              #
         
     | 
| 
       123 
130 
     | 
    
         
             
              # The timer starts immediately upon creation
         
     | 
| 
       124 
131 
     | 
    
         
             
              #
         
     | 
| 
       125 
     | 
    
         
            -
              # @param allowed_seconds [ 
     | 
| 
       126 
     | 
    
         
            -
               
     | 
| 
      
 132 
     | 
    
         
            +
              # @param allowed_seconds [Float, Integer] The total number of seconds to allow
         
     | 
| 
      
 133 
     | 
    
         
            +
              # @param exclude [Enumberable<Symbol>, Symbol, nil] If given a name or
         
     | 
| 
      
 134 
     | 
    
         
            +
              #   list of checkpoint names to skip
         
     | 
| 
      
 135 
     | 
    
         
            +
              # @param only [Enumberable<Symbol>, Symbol, nil] If given a name or
         
     | 
| 
      
 136 
     | 
    
         
            +
              #   list of checkpoint names to allow
         
     | 
| 
      
 137 
     | 
    
         
            +
              def initialize(allowed_seconds, exclude: nil, only: nil)
         
     | 
| 
       127 
138 
     | 
    
         
             
                @allowed_seconds = allowed_seconds.to_f
         
     | 
| 
       128 
139 
     | 
    
         
             
                @start_time = Cutoff.now
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                if exclude
         
     | 
| 
      
 142 
     | 
    
         
            +
                  @exclude = Set.new(exclude.is_a?(Enumerable) ? exclude : [exclude])
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                if only
         
     | 
| 
      
 146 
     | 
    
         
            +
                  @only = Set.new(only.is_a?(Enumerable) ? only : [only])
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
       129 
148 
     | 
    
         
             
              end
         
     | 
| 
       130 
149 
     | 
    
         | 
| 
       131 
150 
     | 
    
         
             
              # The number of seconds left on the clock
         
     | 
| 
         @@ -162,9 +181,29 @@ class Cutoff 
     | 
|
| 
       162 
181 
     | 
    
         
             
              #
         
     | 
| 
       163 
182 
     | 
    
         
             
              # @raise CutoffExceededError If there is an active expired cutoff
         
     | 
| 
       164 
183 
     | 
    
         
             
              # @return [void]
         
     | 
| 
       165 
     | 
    
         
            -
              def checkpoint!
         
     | 
| 
      
 184 
     | 
    
         
            +
              def checkpoint!(name = nil)
         
     | 
| 
      
 185 
     | 
    
         
            +
                unless name.nil? || name.is_a?(Symbol)
         
     | 
| 
      
 186 
     | 
    
         
            +
                  raise ArgumentError, 'name must be a symbol'
         
     | 
| 
      
 187 
     | 
    
         
            +
                end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                return unless selected?(name)
         
     | 
| 
       166 
190 
     | 
    
         
             
                raise CutoffExceededError, self if exceeded?
         
     | 
| 
       167 
191 
     | 
    
         | 
| 
       168 
192 
     | 
    
         
             
                nil
         
     | 
| 
       169 
193 
     | 
    
         
             
              end
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
              # True if the named checkpoint is selected by the `exclude` and `only`
         
     | 
| 
      
 196 
     | 
    
         
            +
              # options. If these options are not given, a checkpoint is considered to be
         
     | 
| 
      
 197 
     | 
    
         
            +
              # selected. If the checkpoint is not named, it is also considered to be
         
     | 
| 
      
 198 
     | 
    
         
            +
              # selected.
         
     | 
| 
      
 199 
     | 
    
         
            +
              #
         
     | 
| 
      
 200 
     | 
    
         
            +
              # @param name [Symbol, nil] The name of the checkpoint
         
     | 
| 
      
 201 
     | 
    
         
            +
              # @return [Boolean] True if the checkpoint is selected
         
     | 
| 
      
 202 
     | 
    
         
            +
              def selected?(name)
         
     | 
| 
      
 203 
     | 
    
         
            +
                return true if name.nil? && @exclude
         
     | 
| 
      
 204 
     | 
    
         
            +
                return false if @exclude&.include?(name)
         
     | 
| 
      
 205 
     | 
    
         
            +
                return false if @only && !@only.include?(name)
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                true
         
     | 
| 
      
 208 
     | 
    
         
            +
              end
         
     | 
| 
       170 
209 
     | 
    
         
             
            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 
     | 
    
         
            +
              version: 0.5.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Justin Howard
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-08-10 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. 
     | 
| 
      
 115 
     | 
    
         
            +
              documentation_uri: https://www.rubydoc.info/gems/cutoff/0.5.0
         
     | 
| 
       115 
116 
     | 
    
         
             
            post_install_message: 
         
     | 
| 
       116 
117 
     | 
    
         
             
            rdoc_options: []
         
     | 
| 
       117 
118 
     | 
    
         
             
            require_paths:
         
     |