async-pool 0.2.0 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/async/pool/controller.rb +88 -7
- data/lib/async/pool/version.rb +1 -1
- metadata +37 -30
- data/.editorconfig +0 -6
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.travis.yml +0 -20
- data/Gemfile +0 -3
- data/README.md +0 -72
- data/Rakefile +0 -6
- data/async-pool.gemspec +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53496ca8f02b057c94d8cef37e92f7d14bb3d7062267ea53f363d5ca3c2ee7d4
|
4
|
+
data.tar.gz: b59bb3a498bb16b1306eea4b6bcb765efa60a479701f546a3c2b6785ff5203e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f53063512c044675725eed75b72346644a36c99704bf2f1559692420f7176b88063cb79200d4a24f19f8cd081be224e307a811d91dc8a94923cccb87cf850cc4
|
7
|
+
data.tar.gz: 366355082c92722989dd76607f611fd81f520844b8733e15872db4164a146bc2e257e40f316bc87c173a49b65d465e07cccdf459fea242b9c8445841c13f7580
|
@@ -20,6 +20,7 @@
|
|
20
20
|
|
21
21
|
require 'async/logger'
|
22
22
|
|
23
|
+
require 'async'
|
23
24
|
require 'async/notification'
|
24
25
|
require 'async/semaphore'
|
25
26
|
|
@@ -31,18 +32,24 @@ module Async
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def initialize(constructor, limit: nil)
|
35
|
+
# All available resources:
|
34
36
|
@resources = {}
|
35
37
|
|
38
|
+
# Resources which may be available to be acquired:
|
39
|
+
# This list may contain false positives, or resources which were okay but have since entered a state which is unusuable.
|
36
40
|
@available = []
|
41
|
+
|
37
42
|
@notification = Async::Notification.new
|
38
43
|
|
39
44
|
@limit = limit
|
40
45
|
|
41
46
|
@constructor = constructor
|
42
47
|
@guard = Async::Semaphore.new(1)
|
48
|
+
|
49
|
+
@gardener = nil
|
43
50
|
end
|
44
51
|
|
45
|
-
# @
|
52
|
+
# @attribute [Hash(Resource, Integer)] all allocated resources, and their associated usage.
|
46
53
|
attr :resources
|
47
54
|
|
48
55
|
def size
|
@@ -63,6 +70,11 @@ module Async
|
|
63
70
|
return false
|
64
71
|
end
|
65
72
|
|
73
|
+
# Whether there are available resources, i.e. whether {#acquire} can reuse an existing resource.
|
74
|
+
def available?
|
75
|
+
@available.any?
|
76
|
+
end
|
77
|
+
|
66
78
|
# Wait until a pool resource has been freed.
|
67
79
|
def wait
|
68
80
|
@notification.wait
|
@@ -97,6 +109,8 @@ module Async
|
|
97
109
|
def close
|
98
110
|
@resources.each_key(&:close)
|
99
111
|
@resources.clear
|
112
|
+
|
113
|
+
@gardener&.stop
|
100
114
|
end
|
101
115
|
|
102
116
|
def to_s
|
@@ -114,7 +128,9 @@ module Async
|
|
114
128
|
unused = []
|
115
129
|
|
116
130
|
@resources.each do |resource, usage|
|
117
|
-
|
131
|
+
if usage.zero?
|
132
|
+
unused << resource
|
133
|
+
end
|
118
134
|
end
|
119
135
|
|
120
136
|
unused.each do |resource|
|
@@ -127,6 +143,14 @@ module Async
|
|
127
143
|
break if @resources.size <= retain
|
128
144
|
end
|
129
145
|
|
146
|
+
# Update availability list:
|
147
|
+
@available.clear
|
148
|
+
@resources.each do |resource, usage|
|
149
|
+
if usage < resource.concurrency and resource.reusable?
|
150
|
+
@available << resource
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
130
154
|
return unused.size
|
131
155
|
end
|
132
156
|
|
@@ -142,6 +166,19 @@ module Async
|
|
142
166
|
|
143
167
|
protected
|
144
168
|
|
169
|
+
def start_gardener
|
170
|
+
return if @gardener
|
171
|
+
|
172
|
+
Async(transient: true) do |task|
|
173
|
+
@gardener = task
|
174
|
+
|
175
|
+
Task.yield
|
176
|
+
ensure
|
177
|
+
@gardener = nil
|
178
|
+
self.close
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
145
182
|
def usage_string
|
146
183
|
"#{@resources.size}/#{@limit || '∞'}"
|
147
184
|
end
|
@@ -152,11 +189,42 @@ module Async
|
|
152
189
|
end.join(";")
|
153
190
|
end
|
154
191
|
|
192
|
+
def usage
|
193
|
+
@resources.count{|resource, usage| usage > 0}
|
194
|
+
end
|
195
|
+
|
196
|
+
def free
|
197
|
+
@resources.count{|resource, usage| usage == 0}
|
198
|
+
end
|
199
|
+
|
200
|
+
# @returns [Boolean] Whether the number of available resources is excessive and we should retire some.
|
201
|
+
def overflowing?
|
202
|
+
if @resources.any?
|
203
|
+
(self.free.to_f / @resources.size) > 0.5
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
155
207
|
def reuse(resource)
|
156
208
|
Async.logger.debug(self) {"Reuse #{resource}"}
|
209
|
+
usage = @resources[resource]
|
210
|
+
|
211
|
+
if usage.zero?
|
212
|
+
raise "Trying to reuse unacquired resource: #{resource}!"
|
213
|
+
end
|
157
214
|
|
158
|
-
|
159
|
-
|
215
|
+
# We retire resources when adding to the @available list would overflow our pool:
|
216
|
+
if usage == 1
|
217
|
+
if overflowing?
|
218
|
+
return retire(resource)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# If the resource was fully utilized, it now becomes available:
|
223
|
+
if usage == resource.concurrency
|
224
|
+
@available.push(resource)
|
225
|
+
end
|
226
|
+
|
227
|
+
@resources[resource] = usage - 1
|
160
228
|
|
161
229
|
@notification.signal
|
162
230
|
end
|
@@ -176,23 +244,35 @@ module Async
|
|
176
244
|
return resource
|
177
245
|
end
|
178
246
|
|
247
|
+
# @returns [Object] A new resource in a "used" state.
|
179
248
|
def create_resource
|
249
|
+
self.start_gardener
|
250
|
+
|
180
251
|
# This might return nil, which means creating the resource failed.
|
181
252
|
if resource = @constructor.call
|
182
253
|
@resources[resource] = 1
|
183
254
|
|
184
|
-
|
255
|
+
# Make the resource available if it can be used multiple times:
|
256
|
+
if resource.concurrency > 1
|
257
|
+
@available.push(resource)
|
258
|
+
end
|
185
259
|
end
|
186
260
|
|
187
261
|
return resource
|
188
262
|
end
|
189
263
|
|
264
|
+
# @returns [Object] An existing resource in a "used" state.
|
190
265
|
def available_resource
|
191
266
|
@guard.acquire do
|
192
267
|
while resource = @available.last
|
193
268
|
if usage = @resources[resource] and usage < resource.concurrency
|
194
269
|
if resource.viable?
|
195
|
-
@resources[resource] += 1
|
270
|
+
usage = (@resources[resource] += 1)
|
271
|
+
|
272
|
+
if usage == resource.concurrency
|
273
|
+
# The resource is used up to it's limit:
|
274
|
+
@available.pop
|
275
|
+
end
|
196
276
|
|
197
277
|
return resource
|
198
278
|
else
|
@@ -200,12 +280,13 @@ module Async
|
|
200
280
|
@available.pop
|
201
281
|
end
|
202
282
|
else
|
283
|
+
# The resource has been removed already, so skip it and remove it from the availability list.
|
203
284
|
@available.pop
|
204
285
|
end
|
205
286
|
end
|
206
287
|
|
207
288
|
if @limit.nil? or @resources.size < @limit
|
208
|
-
Async.logger.debug(self) {"No
|
289
|
+
Async.logger.debug(self) {"No available resources, allocating new one..."}
|
209
290
|
|
210
291
|
return create_resource
|
211
292
|
end
|
data/lib/async/pool/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.25'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.25'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: async-rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: bake-bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: bake-modernize
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,21 +67,21 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: covered
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
@@ -94,29 +94,36 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.6'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.6'
|
111
|
+
description:
|
98
112
|
email:
|
99
|
-
- samuel.williams@oriontransfer.co.nz
|
100
113
|
executables: []
|
101
114
|
extensions: []
|
102
115
|
extra_rdoc_files: []
|
103
116
|
files:
|
104
|
-
- ".editorconfig"
|
105
|
-
- ".gitignore"
|
106
|
-
- ".rspec"
|
107
|
-
- ".travis.yml"
|
108
|
-
- Gemfile
|
109
|
-
- README.md
|
110
|
-
- Rakefile
|
111
|
-
- async-pool.gemspec
|
112
117
|
- lib/async/pool.rb
|
113
118
|
- lib/async/pool/controller.rb
|
114
119
|
- lib/async/pool/resource.rb
|
115
120
|
- lib/async/pool/version.rb
|
116
121
|
homepage: https://github.com/socketry/async-pool
|
117
|
-
licenses:
|
118
|
-
|
119
|
-
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata:
|
125
|
+
funding_uri: https://github.com/sponsors/ioquatix/
|
126
|
+
post_install_message:
|
120
127
|
rdoc_options: []
|
121
128
|
require_paths:
|
122
129
|
- lib
|
@@ -124,15 +131,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
131
|
requirements:
|
125
132
|
- - ">="
|
126
133
|
- !ruby/object:Gem::Version
|
127
|
-
version: '
|
134
|
+
version: '2.5'
|
128
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
136
|
requirements:
|
130
137
|
- - ">="
|
131
138
|
- !ruby/object:Gem::Version
|
132
139
|
version: '0'
|
133
140
|
requirements: []
|
134
|
-
rubygems_version: 3.
|
135
|
-
signing_key:
|
141
|
+
rubygems_version: 3.1.2
|
142
|
+
signing_key:
|
136
143
|
specification_version: 4
|
137
|
-
summary: A
|
144
|
+
summary: A singleplex and multiplex resource pool for implementing robust clients.
|
138
145
|
test_files: []
|
data/.editorconfig
DELETED
data/.gitignore
DELETED
data/.rspec
DELETED
data/.travis.yml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
dist: xenial
|
3
|
-
cache: bundler
|
4
|
-
|
5
|
-
matrix:
|
6
|
-
include:
|
7
|
-
- rvm: 2.3
|
8
|
-
- rvm: 2.4
|
9
|
-
- rvm: 2.5
|
10
|
-
- rvm: 2.6
|
11
|
-
- rvm: 2.6
|
12
|
-
env: COVERAGE=BriefSummary,Coveralls
|
13
|
-
- rvm: ruby-head
|
14
|
-
- rvm: truffleruby
|
15
|
-
- rvm: jruby-head
|
16
|
-
env: JRUBY_OPTS="--debug -X+O"
|
17
|
-
allow_failures:
|
18
|
-
- rvm: ruby-head
|
19
|
-
- rvm: truffleruby
|
20
|
-
- rvm: jruby-head
|
data/Gemfile
DELETED
data/README.md
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
# Async::Pool
|
2
|
-
|
3
|
-
Provides support for connection pooling both singleplex and multiplex resources.
|
4
|
-
|
5
|
-
[![Build Status](https://travis-ci.com/socketry/async-pool.svg)](https://travis-ci.com/socketry/async-pool)
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
gem 'async-pool'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install async-pool
|
22
|
-
|
23
|
-
## Usage
|
24
|
-
|
25
|
-
`Async::Pool::Controller` provides support for both singleplex (one stream at a time) and multiplex resources (multiple streams at a time).
|
26
|
-
|
27
|
-
`Async::Pool::Resource` is provided as an interface and to document how to use the pools. However, you wouldn't need to use this in practice and just implement the appropriate interface on your own objects.
|
28
|
-
|
29
|
-
```ruby
|
30
|
-
pool = Async::Pool::Controller.new(Async::Pool::Resource)
|
31
|
-
|
32
|
-
pool.acquire do |resource|
|
33
|
-
# resource is implicitly released when exiting the block.
|
34
|
-
end
|
35
|
-
|
36
|
-
resource = pool.acquire
|
37
|
-
|
38
|
-
# Return the resource back to the pool:
|
39
|
-
pool.release(resource)
|
40
|
-
```
|
41
|
-
|
42
|
-
## Contributing
|
43
|
-
|
44
|
-
1. Fork it
|
45
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
46
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
47
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
48
|
-
5. Create new Pull Request
|
49
|
-
|
50
|
-
## License
|
51
|
-
|
52
|
-
Released under the MIT license.
|
53
|
-
|
54
|
-
Copyright, 2019, by [Samuel G. D. Williams](http://www.codeotaku.com).
|
55
|
-
|
56
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
57
|
-
of this software and associated documentation files (the "Software"), to deal
|
58
|
-
in the Software without restriction, including without limitation the rights
|
59
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
60
|
-
copies of the Software, and to permit persons to whom the Software is
|
61
|
-
furnished to do so, subject to the following conditions:
|
62
|
-
|
63
|
-
The above copyright notice and this permission notice shall be included in
|
64
|
-
all copies or substantial portions of the Software.
|
65
|
-
|
66
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
67
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
68
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
69
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
70
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
71
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
72
|
-
THE SOFTWARE.
|
data/Rakefile
DELETED
data/async-pool.gemspec
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'lib/async/pool/version'
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = "async-pool"
|
6
|
-
spec.version = Async::Pool::VERSION
|
7
|
-
spec.authors = ["Samuel Williams"]
|
8
|
-
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
9
|
-
|
10
|
-
spec.summary = "A Redis client library."
|
11
|
-
spec.homepage = "https://github.com/socketry/async-pool"
|
12
|
-
|
13
|
-
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
14
|
-
f.match(%r{^(test|spec|features)/})
|
15
|
-
end
|
16
|
-
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.require_paths = ["lib"]
|
19
|
-
|
20
|
-
spec.add_dependency("async", "~> 1.8")
|
21
|
-
|
22
|
-
spec.add_development_dependency "async-rspec", "~> 1.1"
|
23
|
-
|
24
|
-
spec.add_development_dependency "covered"
|
25
|
-
spec.add_development_dependency "bundler"
|
26
|
-
spec.add_development_dependency "rspec", "~> 3.6"
|
27
|
-
spec.add_development_dependency "rake"
|
28
|
-
end
|