restrainer 1.1.0 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
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