restrainer 1.1.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e999370930d2d5686a75214e30aa63223b828f77ba5ba94d48506f5afcca8c9
4
- data.tar.gz: 494b772ddc967646f15f3ba1db809fd34eb6f12a0714242f327326e33af1b648
3
+ metadata.gz: c49c2e29f3d29970f0975f8219d99dc74a79668f263b5c923ab0eb2ff92f8fae
4
+ data.tar.gz: 7af2876a48e9a34d4ff3aad0cd5fb7b12bc5e20c7ee2e875760a59276cc7c601
5
5
  SHA512:
6
- metadata.gz: b2b8dd65c7cd2a5d96daa8c67fe2d7b3a71a77a9035ff00a065b693d179ec1d71f6084ab1d1d8b98f5b9d62bda7628e0d898a0c32d637bc1fcb02032e3e6d7fa
7
- data.tar.gz: e4c85a817f9e0f9b2445e000b8f1f3c2cc79d74023020b8f02fa9f9895e61ee528f6b69aacc88dde8a3fbe6c04cb10f494075e7188bacf37e049486e516a2513
6
+ metadata.gz: 23d0627b6b1cca6b1c8c84ffeb20c595570c2918db27c73ce0a4a9aa158f8cf4df67cdd331d2e3b001106185c8adacffd070b215e2f47ac69b013cee1f3d11c0
7
+ data.tar.gz: c7fc3e5188fa06628d7ab0f5fb4b27b74db917bb95f32ca76875c40be1052237ce83e9018c4bde079e99c09c19514dc1825a2df765f62787c910733e32e4c3f5
data/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 1.1.2
8
+
9
+ ### Changed
10
+ - Redis instance will now default to a Redis instance with the default options
11
+ instead of throwing an error if it was not set.
12
+ - Minumum Ruby version set to 2.5
13
+
14
+ ## 1.1.1
15
+
16
+ ### Fixed
17
+ - Circular reference warning
18
+
19
+ ## 1.1.0
20
+
21
+ ### Added
22
+ - Expose manually locking and unlocking processes.
23
+ - Allow passing in a redis connection in the constructor.
24
+
25
+ ## 1.0.1
26
+
27
+ ### Fixed
28
+ - Use Lua script to avoid race conditions and ensure no extra processes slip through.
29
+
30
+ ## 1.0.0
31
+
32
+ ### Added
33
+ - Initial release.
data/MIT_LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 WHI, Inc.
1
+ Copyright (c) 2015 Brian Durand
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ [![Continuous Integration](https://github.com/bdurand/restrainer/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/restrainer/actions/workflows/continuous_integration.yml)
2
+ [![Regression Test](https://github.com/bdurand/restrainer/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/restrainer/actions/workflows/regression_test.yml)
3
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
4
+
1
5
  This gem provides a method of throttling calls across processes that can be very useful if you have to call an external service with limited resources.
2
6
 
3
7
  A [redis server](http://redis.io/) is required to use this gem.
@@ -59,3 +63,31 @@ restrainer = Restrainer.new(:my_service, 100, timeout: 10)
59
63
  ```
60
64
 
61
65
  This gem does clean up after itself nicely, so that it won't ever leave unused data lying around in redis.
66
+
67
+ ## Installation
68
+
69
+ Add this line to your application's Gemfile:
70
+
71
+ ```ruby
72
+ gem 'restrainer'
73
+ ```
74
+
75
+ And then execute:
76
+ ```bash
77
+ $ bundle
78
+ ```
79
+
80
+ Or install it yourself as:
81
+ ```bash
82
+ $ gem install restrainer
83
+ ```
84
+
85
+ ## Contributing
86
+
87
+ Open a pull request on GitHub.
88
+
89
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
90
+
91
+ ## License
92
+
93
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.2
data/lib/restrainer.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'redis'
4
- require 'securerandom'
3
+ require "redis"
4
+ require "securerandom"
5
5
 
6
6
  # Redis backed throttling mechanism to ensure that only a limited number of processes can
7
7
  # be executed at any one time.
@@ -14,7 +14,6 @@ require 'securerandom'
14
14
  # If more than the specified number of processes as identified by the name argument is currently
15
15
  # running, then the throttle block will raise an error.
16
16
  class Restrainer
17
-
18
17
  attr_reader :name, :limit
19
18
 
20
19
  ADD_PROCESS_SCRIPT = <<-LUA
@@ -41,7 +40,7 @@ class Restrainer
41
40
  redis.call('zadd', sorted_set, now, process_id)
42
41
  redis.call('expire', sorted_set, ttl)
43
42
  end
44
-
43
+
45
44
  -- Return the number of processes running before the process was added.
46
45
  return process_count
47
46
  LUA
@@ -53,60 +52,75 @@ class Restrainer
53
52
  class ThrottledError < StandardError
54
53
  end
55
54
 
55
+ @redis = nil
56
+
56
57
  class << self
57
58
  # Either configure the redis instance using a block or yield the instance. Configuring with
58
59
  # a block allows you to use things like connection pools etc. without hard coding a single
59
60
  # instance.
60
61
  #
61
- # Example: `Restrainer.redis { redis_pool.instance }`
62
+ # @param block [Proc] A block that returns a redis instance.
63
+ # @return [Redis] The redis instance.
64
+ # @example
65
+ # Restrainer.redis { redis_pool.instance }
62
66
  def redis(&block)
63
67
  if block
64
68
  @redis = block
65
- elsif defined?(@redis) && @redis
66
- @redis.call
67
69
  else
68
- raise "#{self.class.name}.redis not configured"
70
+ unless @redis
71
+ client = Redis.new
72
+ @redis = lambda { client }
73
+ end
74
+ @redis.call
69
75
  end
70
76
  end
71
77
 
72
- # Set the redis instance to a specific instance. It is usually preferable to use the block
73
- # form for configurating the instance so that it can be evaluated at runtime.
78
+ # Set the redis instance to a specific instance. It is usually preferable to use
79
+ # the block form for configurating the instance so that it can be evaluated at
80
+ # runtime.
74
81
  #
75
- # Example: `Restrainer.redis = Redis.new`
82
+ # @param conn [Redis]
83
+ # @return [void]
84
+ # @example
85
+ # Restrainer.redis = Redis.new
76
86
  def redis=(conn)
77
- @redis = lambda{ conn }
87
+ @redis = lambda { conn }
78
88
  end
79
89
  end
80
90
 
81
- # Create a new restrainer. The name is used to identify the Restrainer and group processes together.
82
- # You can create any number of Restrainers with different names.
91
+ # Create a new restrainer. The name is used to identify the Restrainer and group
92
+ # processes together. You can create any number of Restrainers with different names.
83
93
  #
84
- # The required limit parameter specifies the maximum number of processes that will be allowed to execute the
85
- # throttle block at any point in time.
94
+ # The required limit parameter specifies the maximum number of processes that
95
+ # will be allowed to execute the throttle block at any point in time.
86
96
  #
87
- # The timeout parameter is used for cleaning up internal data structures so that jobs aren't orphaned
88
- # if their process is killed. Processes will automatically be removed from the running jobs list after the
89
- # specified number of seconds. Note that the Restrainer will not handle timing out any code itself. This
97
+ # The timeout parameter is used for cleaning up internal data structures so that
98
+ # jobs aren't orphaned if their process is killed. Processes will automatically
99
+ # be removed from the running jobs list after the specified number of seconds.
100
+ # Note that the Restrainer will not handle timing out any code itself. This
90
101
  # value is just used to insure the integrity of internal data structures.
91
102
  def initialize(name, limit:, timeout: 60, redis: nil)
92
103
  @name = name
93
104
  @limit = limit
94
105
  @timeout = timeout
95
- @key = "#{self.class.name}.#{name.to_s}"
96
- @redis ||= redis
106
+ @key = "#{self.class.name}.#{name}"
107
+ @redis = redis
97
108
  end
98
109
 
99
- # Wrap a block with this method to throttle concurrent execution. If more than the alotted number
100
- # of processes (as identified by the name) are currently executing, then a Restrainer::ThrottledError
101
- # will be raised.
110
+ # Wrap a block with this method to throttle concurrent execution. If more than the
111
+ # alotted number of processes (as identified by the name) are currently executing,
112
+ # then a Restrainer::ThrottledError will be raised.
102
113
  #
103
- # The limit argument can be used to override the value set in the constructor.
114
+ # @param limit [Integer] The maximum number of processes that can be executing
115
+ # at any one time. Defaults to the value passed to the constructor.
116
+ # @return [void]
117
+ # @raise [Restrainer::ThrottledError] If the throttle would be exceeded.
104
118
  def throttle(limit: nil)
105
119
  limit ||= self.limit
106
120
 
107
121
  # limit of less zero is no limit; limit of zero is allow none
108
122
  return yield if limit < 0
109
-
123
+
110
124
  process_id = lock!(limit: limit)
111
125
  begin
112
126
  yield
@@ -114,15 +128,18 @@ class Restrainer
114
128
  release!(process_id)
115
129
  end
116
130
  end
117
-
131
+
118
132
  # Obtain a lock on one the allowed processes. The method returns a process
119
133
  # identifier that must be passed to the release! to release the lock.
120
134
  # You can pass in a unique identifier if you already have one.
121
135
  #
122
- # Raises a Restrainer::ThrottledError if the lock cannot be obtained.
123
- #
124
- # The limit argument can be used to override the value set in the constructor.
125
- def lock!(process_id = nil, limit: limit)
136
+ # @param process_id [String] A unique identifier for the process. If none is
137
+ # passed, a unique value will be generated.
138
+ # @param limit [Integer] The maximum number of processes that can be executing
139
+ # at any one time. Defaults to the value passed to the constructor.
140
+ # @return [String] The process identifier.
141
+ # @raise [Restrainer::ThrottledError] If the throttle would be exceeded.
142
+ def lock!(process_id = nil, limit: nil)
126
143
  process_id ||= SecureRandom.uuid
127
144
  limit ||= self.limit
128
145
 
@@ -133,18 +150,28 @@ class Restrainer
133
150
  add_process!(redis, process_id, limit)
134
151
  process_id
135
152
  end
136
-
137
- # release one of the allowed processes. You must pass in a process id returned by the lock method.
153
+
154
+ # Release one of the allowed processes. You must pass in a process id
155
+ # returned by the lock method.
156
+ #
157
+ # @param process_id [String] The process identifier returned by the lock call.
158
+ # @return [Boolean] True if the process was released, false if it was not found.
138
159
  def release!(process_id)
139
- remove_process!(redis, process_id) unless process_id.nil?
160
+ return false if process_id.nil?
161
+
162
+ remove_process!(redis, process_id)
140
163
  end
141
164
 
142
165
  # Get the number of processes currently being executed for this restrainer.
166
+ #
167
+ # @return [Integer] The number of processes currently being executed.
143
168
  def current
144
169
  redis.zcard(key).to_i
145
170
  end
146
-
147
- # Clear all locks
171
+
172
+ # Clear all locks.
173
+ #
174
+ # @return [void]
148
175
  def clear!
149
176
  redis.del(key)
150
177
  end
@@ -154,29 +181,29 @@ class Restrainer
154
181
  def redis
155
182
  @redis || self.class.redis
156
183
  end
157
-
184
+
158
185
  # Hash key in redis to story a sorted set of current processes.
159
- def key
160
- @key
161
- end
186
+ attr_reader :key
162
187
 
163
188
  # Add a process to the currently run set.
164
189
  def add_process!(redis, process_id, throttle_limit)
165
- process_count = eval_script(redis, process_id, throttle_limit)
190
+ process_count = eval_script(redis, process_id, throttle_limit)
166
191
  if process_count >= throttle_limit
167
192
  raise ThrottledError.new("#{self.class}: #{@name} already has #{process_count} processes running")
168
193
  end
169
194
  end
170
195
 
171
- # Remove a process to the currently run set.
196
+ # Remove a process to the currently run set. Returns true if the process was removed.
172
197
  def remove_process!(redis, process_id)
173
- redis.zrem(key, process_id)
198
+ result = redis.zrem(key, process_id)
199
+ result = true if result == 1
200
+ result
174
201
  end
175
202
 
176
203
  # Evaluate and execute a Lua script on the redis server.
177
204
  def eval_script(redis, process_id, throttle_limit)
178
205
  sha1 = @add_process_sha1
179
- if sha1 == nil
206
+ if sha1.nil?
180
207
  sha1 = redis.script(:load, ADD_PROCESS_SCRIPT)
181
208
  @add_process_sha1 = sha1
182
209
  end
@@ -184,7 +211,7 @@ class Restrainer
184
211
  begin
185
212
  redis.evalsha(sha1, [], [key, process_id, throttle_limit, @timeout, Time.now.to_i])
186
213
  rescue Redis::CommandError => e
187
- if e.message.include?('NOSCRIPT')
214
+ if e.message.include?("NOSCRIPT")
188
215
  sha1 = redis.script(:load, ADD_PROCESS_SCRIPT)
189
216
  @add_process_sha1 = sha1
190
217
  retry
data/restrainer.gemspec CHANGED
@@ -1,27 +1,34 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
-
5
1
  Gem::Specification.new do |spec|
6
- spec.name = "restrainer"
7
- spec.version = File.read(File.expand_path("../VERSION", __FILE__)).chomp
8
- spec.authors = ["We Heart It", "Brian Durand"]
9
- spec.email = ["dev@weheartit.com", "bbdurand@gmail.com"]
10
- spec.summary = "Code for throttling workloads so as not to overwhelm external services"
11
- spec.description = "Code for throttling workloads so as not to overwhelm external services."
12
- spec.homepage = "https://github.com/weheartit/restrainer"
13
- spec.license = "MIT"
14
- spec.files = `git ls-files`.split($/)
15
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
2
+ spec.name = "restrainer"
3
+ spec.version = File.read(File.expand_path("VERSION", __dir__)).strip
4
+ spec.authors = ["Brian Durand"]
5
+ spec.email = ["bbdurand@gmail.com"]
6
+
7
+ spec.summary = "Code for throttling workloads so as not to overwhelm external services"
8
+ spec.homepage = "https://github.com/bdurand/restrainer"
9
+ spec.license = "MIT"
10
+
11
+ # Specify which files should be added to the gem when it is released.
12
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
13
+ ignore_files = %w[
14
+ .
15
+ Appraisals
16
+ Gemfile
17
+ Gemfile.lock
18
+ Rakefile
19
+ bin/
20
+ gemfiles/
21
+ spec/
22
+ ]
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
25
+ end
26
+
17
27
  spec.require_paths = ["lib"]
18
28
 
19
- spec.required_ruby_version = '>=2.0'
29
+ spec.required_ruby_version = ">= 2.5"
20
30
 
21
- spec.add_dependency('redis')
31
+ spec.add_dependency("redis")
22
32
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec"
26
- spec.add_development_dependency "timecop"
33
+ spec.add_development_dependency "bundler"
27
34
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restrainer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
- - We Heart It
8
7
  - Brian Durand
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2019-07-17 00:00:00.000000000 Z
11
+ date: 2023-11-11 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: redis
@@ -27,48 +26,6 @@ dependencies:
27
26
  version: '0'
28
27
  - !ruby/object:Gem::Dependency
29
28
  name: bundler
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '1.3'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '1.3'
42
- - !ruby/object:Gem::Dependency
43
- name: rake
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- version: '0'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: '0'
56
- - !ruby/object:Gem::Dependency
57
- name: rspec
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: '0'
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- - !ruby/object:Gem::Dependency
71
- name: timecop
72
29
  requirement: !ruby/object:Gem::Requirement
73
30
  requirements:
74
31
  - - ">="
@@ -81,29 +38,24 @@ dependencies:
81
38
  - - ">="
82
39
  - !ruby/object:Gem::Version
83
40
  version: '0'
84
- description: Code for throttling workloads so as not to overwhelm external services.
41
+ description:
85
42
  email:
86
- - dev@weheartit.com
87
43
  - bbdurand@gmail.com
88
44
  executables: []
89
45
  extensions: []
90
46
  extra_rdoc_files: []
91
47
  files:
92
- - ".gitignore"
93
- - CHANGE_LOG.md
48
+ - CHANGELOG.md
94
49
  - MIT_LICENSE.txt
95
50
  - README.md
96
- - Rakefile
97
51
  - VERSION
98
52
  - lib/restrainer.rb
99
53
  - restrainer.gemspec
100
- - spec/restrainer_spec.rb
101
- - spec/spec_helper.rb
102
- homepage: https://github.com/weheartit/restrainer
54
+ homepage: https://github.com/bdurand/restrainer
103
55
  licenses:
104
56
  - MIT
105
57
  metadata: {}
106
- post_install_message:
58
+ post_install_message:
107
59
  rdoc_options: []
108
60
  require_paths:
109
61
  - lib
@@ -111,17 +63,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
63
  requirements:
112
64
  - - ">="
113
65
  - !ruby/object:Gem::Version
114
- version: '2.0'
66
+ version: '2.5'
115
67
  required_rubygems_version: !ruby/object:Gem::Requirement
116
68
  requirements:
117
69
  - - ">="
118
70
  - !ruby/object:Gem::Version
119
71
  version: '0'
120
72
  requirements: []
121
- rubygems_version: 3.0.3
122
- signing_key:
73
+ rubygems_version: 3.4.12
74
+ signing_key:
123
75
  specification_version: 4
124
76
  summary: Code for throttling workloads so as not to overwhelm external services
125
- test_files:
126
- - spec/restrainer_spec.rb
127
- - spec/spec_helper.rb
77
+ test_files: []
data/.gitignore DELETED
@@ -1,17 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
data/CHANGE_LOG.md DELETED
@@ -1,9 +0,0 @@
1
- # 1.1.0
2
-
3
- * Expose manually locking and unlocking processes.
4
-
5
- * Allow passing in a redis connection in the constructor.
6
-
7
- # 1.0.1
8
-
9
- * Use Lua script to avoid race conditions and ensure no extra processes slip through.
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Restrainer do
6
-
7
- before(:each) do
8
- Restrainer.new(:restrainer_test, limit: 1).clear!
9
- end
10
-
11
- it "should have a name and max_processes" do
12
- restrainer = Restrainer.new(:restrainer_test, limit: 1)
13
- expect(restrainer.name).to eq(:restrainer_test)
14
- expect(restrainer.limit).to eq(1)
15
- end
16
-
17
- it "should run a block!" do
18
- restrainer = Restrainer.new(:restrainer_test, limit: 1)
19
- x = nil
20
- expect(restrainer.throttle{ x = restrainer.current }).to eq(1)
21
- expect(x).to eq(1)
22
- expect(restrainer.current).to eq(0)
23
- end
24
-
25
- it "should throw an error if too many processes are already running" do
26
- restrainer = Restrainer.new(:restrainer_test, limit: 5)
27
- x = nil
28
- restrainer.throttle do
29
- restrainer.throttle do
30
- restrainer.throttle do
31
- restrainer.throttle do
32
- restrainer.throttle do
33
- expect(lambda{restrainer.throttle{ x = 1 }}).to raise_error(Restrainer::ThrottledError)
34
- end
35
- end
36
- end
37
- end
38
- end
39
- expect(x).to eq(nil)
40
- end
41
-
42
- it "should not throw an error if the number of processes is under the limit" do
43
- restrainer = Restrainer.new(:restrainer_test, limit: 2)
44
- x = nil
45
- restrainer.throttle do
46
- restrainer.throttle{ x = 1 }
47
- end
48
- expect(x).to eq(1)
49
- end
50
-
51
- it "should let the throttle method override the limit" do
52
- restrainer = Restrainer.new(:restrainer_test, limit: 1)
53
- x = nil
54
- restrainer.throttle do
55
- restrainer.throttle(limit: 2){ x = 1 }
56
- end
57
- expect(x).to eq(1)
58
- end
59
-
60
- it "should allow processing to be turned off entirely by setting the limit to zero" do
61
- restrainer = Restrainer.new(:restrainer_test, limit: 1)
62
- x = nil
63
- expect(lambda{restrainer.throttle(limit: 0){ x = 1 }}).to raise_error(Restrainer::ThrottledError)
64
- expect(x).to eq(nil)
65
- end
66
-
67
- it "should allow the throttle to be opened up entirely with a negative limit" do
68
- restrainer = Restrainer.new(:restrainer_test, limit: 0)
69
- x = nil
70
- restrainer.throttle(limit: -1){ x = 1 }
71
- expect(x).to eq(1)
72
- end
73
-
74
- it "should cleanup the running process list if orphaned processes exist" do
75
- restrainer = Restrainer.new(:restrainer_test, limit: 1, timeout: 10)
76
- x = nil
77
- restrainer.throttle do
78
- Timecop.travel(11) do
79
- restrainer.throttle{ x = 1 }
80
- end
81
- end
82
- expect(x).to eq(1)
83
- end
84
-
85
- it "should be able to lock! and release! processes manually" do
86
- restrainer = Restrainer.new(:restrainer_test, limit: 5)
87
- p1 = restrainer.lock!
88
- begin
89
- p2 = restrainer.lock!
90
- begin
91
- p3 = restrainer.lock!
92
- begin
93
- p4 = restrainer.lock!
94
- begin
95
- p5 = restrainer.lock!
96
- begin
97
- expect{ restrainer.lock! }.to raise_error(Restrainer::ThrottledError)
98
- ensure
99
- restrainer.release!(p5)
100
- end
101
- p6 = restrainer.lock!
102
- restrainer.release!(p6)
103
- ensure
104
- restrainer.release!(p4)
105
- end
106
- ensure
107
- restrainer.release!(p3)
108
- end
109
- ensure
110
- restrainer.release!(p2)
111
- end
112
- ensure
113
- restrainer.release!(p1)
114
- end
115
- end
116
-
117
- it "should be able to pass in the process id" do
118
- restrainer = Restrainer.new(:restrainer_test, limit: 1)
119
- expect(restrainer.lock!("foo")).to eq "foo"
120
- end
121
-
122
- it "should not get a lock! if the limit is 0" do
123
- restrainer = Restrainer.new(:restrainer_test, limit: 0)
124
- expect{ restrainer.lock! }.to raise_error(Restrainer::ThrottledError)
125
- end
126
-
127
- it "should get a lock! if the limit is negative" do
128
- restrainer = Restrainer.new(:restrainer_test, limit: -1)
129
- process_id = restrainer.lock!
130
- expect(process_id).to eq nil
131
- restrainer.release!(nil)
132
- end
133
-
134
- it "should be able to override the limit in lock!" do
135
- restrainer = Restrainer.new(:restrainer_test, limit: 0)
136
- restrainer.lock!(limit: 1)
137
- end
138
- end
data/spec/spec_helper.rb DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../../lib/restrainer', __FILE__)
4
- require 'timecop'
5
-
6
- RSpec.configure do |config|
7
- config.run_all_when_everything_filtered = true
8
- config.filter_run :focus
9
-
10
- # Run specs in random order to surface order dependencies. If you find an
11
- # order dependency and want to debug it, you can fix the order by providing
12
- # the seed, which is printed after each run.
13
- # --seed 1234
14
- config.order = 'random'
15
-
16
- Restrainer.redis = Redis.new
17
- end