restrainer 1.1.1 → 1.1.3

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: b1cd37f4ccca910bb1a619d15cd176f656eae9c28832a493ae0ce14901afabaf
4
- data.tar.gz: f55d26dfa66944e52fe1de59d791b7f8215f7afd4b0f13bb2c4cc77833c908d8
3
+ metadata.gz: fd2f59427326450c5159980282e4d463d275dac6fceaa2a31423a5433ff1502f
4
+ data.tar.gz: ddf65e8c8915e920a9689f78bce879d6feae920740890acc7c3df5f5c8ccb2d4
5
5
  SHA512:
6
- metadata.gz: 073671e4736562c7619f732b4fd4af031afc04617e294e4ff779f8b2c4a815c090c5ccc8bb9a7426a8c7777a86258a84c28733458b0e56c34fb2c64aa2763aec
7
- data.tar.gz: d48174a050a831d91dc1b237aa63a584ad831936a3c310b999af7ac96f418b06c04251b98963c581a41833a95577b21b5573c7d1c5b53928e9fc432d3a169656
6
+ metadata.gz: 4166271f5f617a48286024f8e57ab7ebc662c0edfd357ced9e4e40d3998b975db1abcb4db6a1c8de2922ffc57ace7593e752798f39a6f552659877a146fff1b5
7
+ data.tar.gz: 032357eb1992399ba5d10681b7194e667d852ca000b51dcc6ac592f8d10b4734306225208c918d9c3f73faec11adf690a6d0a1ffccefab57deef09438854437a
data/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
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.3
8
+
9
+ ### Added
10
+
11
+ - Support for using fractional seconds in the lock timeout.
12
+
13
+ ## 1.1.2
14
+
15
+ ### Changed
16
+
17
+ - Redis instance will now default to a Redis instance with the default options
18
+ instead of throwing an error if it was not set.
19
+ - Minumum Ruby version set to 2.5
20
+
21
+ ## 1.1.1
22
+
23
+ ### Fixed
24
+
25
+ - Circular reference warning
26
+
27
+ ## 1.1.0
28
+
29
+ ### Added
30
+
31
+ - Expose manually locking and unlocking processes.
32
+ - Allow passing in a redis connection in the constructor.
33
+
34
+ ## 1.0.1
35
+
36
+ ### Fixed
37
+
38
+ - Use Lua script to avoid race conditions and ensure no extra processes slip through.
39
+
40
+ ## 1.0.0
41
+
42
+ ### Added
43
+
44
+ - 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,8 @@
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
+ [![Gem Version](https://badge.fury.io/rb/restrainer.svg)](https://badge.fury.io/rb/restrainer)
5
+
1
6
  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
7
 
3
8
  A [redis server](http://redis.io/) is required to use this gem.
@@ -59,3 +64,31 @@ restrainer = Restrainer.new(:my_service, 100, timeout: 10)
59
64
  ```
60
65
 
61
66
  This gem does clean up after itself nicely, so that it won't ever leave unused data lying around in redis.
67
+
68
+ ## Installation
69
+
70
+ Add this line to your application's Gemfile:
71
+
72
+ ```ruby
73
+ gem 'restrainer'
74
+ ```
75
+
76
+ And then execute:
77
+ ```bash
78
+ $ bundle
79
+ ```
80
+
81
+ Or install it yourself as:
82
+ ```bash
83
+ $ gem install restrainer
84
+ ```
85
+
86
+ ## Contributing
87
+
88
+ Open a pull request on GitHub.
89
+
90
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
91
+
92
+ ## License
93
+
94
+ 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.1
1
+ 1.1.3
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
@@ -39,7 +38,7 @@ class Restrainer
39
38
  -- Success so add to the list and set a global expiration so the list cleans up after itself.
40
39
  if process_count < limit then
41
40
  redis.call('zadd', sorted_set, now, process_id)
42
- redis.call('expire', sorted_set, ttl)
41
+ redis.call('pexpire', sorted_set, math.ceil(ttl * 1000))
43
42
  end
44
43
 
45
44
  -- Return the number of processes running before the process was added.
@@ -53,54 +52,69 @@ 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
 
@@ -119,9 +133,12 @@ class Restrainer
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.
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.
125
142
  def lock!(process_id = nil, limit: nil)
126
143
  process_id ||= SecureRandom.uuid
127
144
  limit ||= self.limit
@@ -134,17 +151,30 @@ class Restrainer
134
151
  process_id
135
152
  end
136
153
 
137
- # release one of the allowed processes. You must pass in a process id returned by the lock method.
154
+ def lock(process_id = nil, limit: nil)
155
+ end
156
+
157
+ # Release one of the allowed processes. You must pass in a process id
158
+ # returned by the lock method.
159
+ #
160
+ # @param process_id [String] The process identifier returned by the lock call.
161
+ # @return [Boolean] True if the process was released, false if it was not found.
138
162
  def release!(process_id)
139
- remove_process!(redis, process_id) unless process_id.nil?
163
+ return false if process_id.nil?
164
+
165
+ remove_process!(redis, process_id)
140
166
  end
141
167
 
142
168
  # Get the number of processes currently being executed for this restrainer.
169
+ #
170
+ # @return [Integer] The number of processes currently being executed.
143
171
  def current
144
172
  redis.zcard(key).to_i
145
173
  end
146
174
 
147
- # Clear all locks
175
+ # Clear all locks.
176
+ #
177
+ # @return [void]
148
178
  def clear!
149
179
  redis.del(key)
150
180
  end
@@ -156,9 +186,7 @@ class Restrainer
156
186
  end
157
187
 
158
188
  # Hash key in redis to story a sorted set of current processes.
159
- def key
160
- @key
161
- end
189
+ attr_reader :key
162
190
 
163
191
  # Add a process to the currently run set.
164
192
  def add_process!(redis, process_id, throttle_limit)
@@ -168,23 +196,25 @@ class Restrainer
168
196
  end
169
197
  end
170
198
 
171
- # Remove a process to the currently run set.
199
+ # Remove a process to the currently run set. Returns true if the process was removed.
172
200
  def remove_process!(redis, process_id)
173
- redis.zrem(key, process_id)
201
+ result = redis.zrem(key, process_id)
202
+ result = true if result == 1
203
+ result
174
204
  end
175
205
 
176
206
  # Evaluate and execute a Lua script on the redis server.
177
207
  def eval_script(redis, process_id, throttle_limit)
178
208
  sha1 = @add_process_sha1
179
- if sha1 == nil
209
+ if sha1.nil?
180
210
  sha1 = redis.script(:load, ADD_PROCESS_SCRIPT)
181
211
  @add_process_sha1 = sha1
182
212
  end
183
213
 
184
214
  begin
185
- redis.evalsha(sha1, [], [key, process_id, throttle_limit, @timeout, Time.now.to_i])
215
+ redis.evalsha(sha1, [], [key, process_id, throttle_limit, @timeout, Time.now.to_f])
186
216
  rescue Redis::CommandError => e
187
- if e.message.include?('NOSCRIPT')
217
+ if e.message.include?("NOSCRIPT")
188
218
  sha1 = redis.script(:load, ADD_PROCESS_SCRIPT)
189
219
  @add_process_sha1 = sha1
190
220
  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.1
4
+ version: 1.1.3
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-12-22 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.20
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,13 +0,0 @@
1
- # 1.1.1
2
-
3
- * Circular reference warning fix
4
-
5
- # 1.1.0
6
-
7
- * Expose manually locking and unlocking processes.
8
-
9
- * Allow passing in a redis connection in the constructor.
10
-
11
- # 1.0.1
12
-
13
- * 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,19 +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
- redis_opts = {}
17
- redis_opts = {url: ENV["REDIS_URL"]} if ENV["REDIS_URL"]
18
- Restrainer.redis = Redis.new(redis_opts)
19
- end