async-pool 0.3.0 → 0.3.5

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: 731a576d4114fb425b42892ddcaf142b716f8917bfd2b71b350b20693695d89c
4
- data.tar.gz: a4741eac4eb25dafe84c6581c1b0483c0ff3f6e26a9477ea96af245d74f1130f
3
+ metadata.gz: 187ab4a00163af3f7b15cef9764a8ba46a33fe353c5784da7efa723f59bf4d07
4
+ data.tar.gz: d74bdfc5c5796709ec64e884e4b35b24d6fd438839135503a26cc5f7519dfda0
5
5
  SHA512:
6
- metadata.gz: ca20d5353fac4bb839fa6b846a48c89ef48dc8ad86632420e504a4eb079e1be6d3cdd99d57f85238a3d5c93fa00e878dc297444bf188a12243bcc907cee6bf47
7
- data.tar.gz: adf70450fdc1ca0def9eb6548d023bacfde995e2446f9b88db56ebc2f1f1e91436a780105ceec41580fe52c6acfafe64c265e02858b3a92802a93b43964faec8
6
+ metadata.gz: d089f3cbb49ae94b64527a6782f4d3f68c133b96b1a00b17c036356d233c50fde88feb28eb3f65ed367f18760f482b8328e68ba83fa9cd7d0090c57be4aa0f0b
7
+ data.tar.gz: e72e69324a4c23b6bd20175189a6350e73b6a2499485587f1465d14aa792dfcd5f7a6866ce4b0fa2ae420461df8d389209d8adf95a9d321a07fa54648227a4ce
@@ -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,9 +32,13 @@ 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
@@ -44,7 +49,7 @@ module Async
44
49
  @gardener = nil
45
50
  end
46
51
 
47
- # @attr [Hash<Resource, Integer>] all allocated resources, and their associated usage.
52
+ # @attribute [Hash(Resource, Integer)] all allocated resources, and their associated usage.
48
53
  attr :resources
49
54
 
50
55
  def size
@@ -65,6 +70,11 @@ module Async
65
70
  return false
66
71
  end
67
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
+
68
78
  # Wait until a pool resource has been freed.
69
79
  def wait
70
80
  @notification.wait
@@ -97,6 +107,8 @@ module Async
97
107
  end
98
108
 
99
109
  def close
110
+ @available.clear
111
+
100
112
  @resources.each_key(&:close)
101
113
  @resources.clear
102
114
 
@@ -118,7 +130,9 @@ module Async
118
130
  unused = []
119
131
 
120
132
  @resources.each do |resource, usage|
121
- unused << resource if usage.zero?
133
+ if usage.zero?
134
+ unused << resource
135
+ end
122
136
  end
123
137
 
124
138
  unused.each do |resource|
@@ -131,6 +145,14 @@ module Async
131
145
  break if @resources.size <= retain
132
146
  end
133
147
 
148
+ # Update availability list:
149
+ @available.clear
150
+ @resources.each do |resource, usage|
151
+ if usage < resource.concurrency and resource.reusable?
152
+ @available << resource
153
+ end
154
+ end
155
+
134
156
  return unused.size
135
157
  end
136
158
 
@@ -149,7 +171,7 @@ module Async
149
171
  def start_gardener
150
172
  return if @gardener
151
173
 
152
- Async(transient: true) do |task|
174
+ Async(transient: true, annotation: "#{self.class} Gardener") do |task|
153
175
  @gardener = task
154
176
 
155
177
  Task.yield
@@ -169,11 +191,42 @@ module Async
169
191
  end.join(";")
170
192
  end
171
193
 
194
+ def usage
195
+ @resources.count{|resource, usage| usage > 0}
196
+ end
197
+
198
+ def free
199
+ @resources.count{|resource, usage| usage == 0}
200
+ end
201
+
202
+ # @returns [Boolean] Whether the number of available resources is excessive and we should retire some.
203
+ def overflowing?
204
+ if @resources.any?
205
+ (self.free.to_f / @resources.size) > 0.5
206
+ end
207
+ end
208
+
172
209
  def reuse(resource)
173
210
  Async.logger.debug(self) {"Reuse #{resource}"}
211
+ usage = @resources[resource]
212
+
213
+ if usage.zero?
214
+ raise "Trying to reuse unacquired resource: #{resource}!"
215
+ end
216
+
217
+ # We retire resources when adding to the @available list would overflow our pool:
218
+ if usage == 1
219
+ if overflowing?
220
+ return retire(resource)
221
+ end
222
+ end
223
+
224
+ # If the resource was fully utilized, it now becomes available:
225
+ if usage == resource.concurrency
226
+ @available.push(resource)
227
+ end
174
228
 
175
- @resources[resource] -= 1
176
- @available.push(resource)
229
+ @resources[resource] = usage - 1
177
230
 
178
231
  @notification.signal
179
232
  end
@@ -193,6 +246,7 @@ module Async
193
246
  return resource
194
247
  end
195
248
 
249
+ # @returns [Object] A new resource in a "used" state.
196
250
  def create_resource
197
251
  self.start_gardener
198
252
 
@@ -200,18 +254,27 @@ module Async
200
254
  if resource = @constructor.call
201
255
  @resources[resource] = 1
202
256
 
203
- @available.push(resource) if resource.concurrency > 1
257
+ # Make the resource available if it can be used multiple times:
258
+ if resource.concurrency > 1
259
+ @available.push(resource)
260
+ end
204
261
  end
205
262
 
206
263
  return resource
207
264
  end
208
265
 
266
+ # @returns [Object] An existing resource in a "used" state.
209
267
  def available_resource
210
268
  @guard.acquire do
211
269
  while resource = @available.last
212
270
  if usage = @resources[resource] and usage < resource.concurrency
213
271
  if resource.viable?
214
- @resources[resource] += 1
272
+ usage = (@resources[resource] += 1)
273
+
274
+ if usage == resource.concurrency
275
+ # The resource is used up to it's limit:
276
+ @available.pop
277
+ end
215
278
 
216
279
  return resource
217
280
  else
@@ -219,12 +282,13 @@ module Async
219
282
  @available.pop
220
283
  end
221
284
  else
285
+ # The resource has been removed already, so skip it and remove it from the availability list.
222
286
  @available.pop
223
287
  end
224
288
  end
225
289
 
226
290
  if @limit.nil? or @resources.size < @limit
227
- Async.logger.debug(self) {"No resources resources, allocating new one..."}
291
+ Async.logger.debug(self) {"No available resources, allocating new one..."}
228
292
 
229
293
  return create_resource
230
294
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module Pool
23
- VERSION = "0.3.0"
23
+ VERSION = "0.3.5"
24
24
  end
25
25
  end
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.3.0
4
+ version: 0.3.5
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: 2020-04-23 00:00:00.000000000 Z
11
+ date: 2021-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.1'
41
41
  - !ruby/object:Gem::Dependency
42
- name: covered
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: bundler
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: rspec
70
+ name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '3.6'
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: '3.6'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: bake-bundler
84
+ name: covered
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -94,28 +94,36 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description:
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
- - ".github/workflows/development.yml"
106
- - ".gitignore"
107
- - ".rspec"
108
- - Gemfile
109
- - README.md
110
- - async-pool.gemspec
111
117
  - lib/async/pool.rb
112
118
  - lib/async/pool/controller.rb
113
119
  - lib/async/pool/resource.rb
114
120
  - lib/async/pool/version.rb
115
121
  homepage: https://github.com/socketry/async-pool
116
- licenses: []
117
- metadata: {}
118
- post_install_message:
122
+ licenses:
123
+ - MIT
124
+ metadata:
125
+ funding_uri: https://github.com/sponsors/ioquatix/
126
+ post_install_message:
119
127
  rdoc_options: []
120
128
  require_paths:
121
129
  - lib
@@ -123,7 +131,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
131
  requirements:
124
132
  - - ">="
125
133
  - !ruby/object:Gem::Version
126
- version: '0'
134
+ version: '2.5'
127
135
  required_rubygems_version: !ruby/object:Gem::Requirement
128
136
  requirements:
129
137
  - - ">="
@@ -131,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
139
  version: '0'
132
140
  requirements: []
133
141
  rubygems_version: 3.1.2
134
- signing_key:
142
+ signing_key:
135
143
  specification_version: 4
136
- summary: A Redis client library.
144
+ summary: A singleplex and multiplex resource pool for implementing robust clients.
137
145
  test_files: []
data/.editorconfig DELETED
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
@@ -1,51 +0,0 @@
1
- name: Development
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- test:
7
- runs-on: ${{matrix.os}}-latest
8
- continue-on-error: ${{matrix.experimental}}
9
-
10
- strategy:
11
- matrix:
12
- os:
13
- - ubuntu
14
- - macos
15
-
16
- ruby:
17
- - 2.5
18
- - 2.6
19
- - 2.7
20
-
21
- experimental: [false]
22
- env: [""]
23
-
24
- include:
25
- - os: ubuntu
26
- ruby: truffleruby
27
- experimental: true
28
- env: JRUBY_OPTS="--debug -X+O"
29
- - os: ubuntu
30
- ruby: jruby
31
- experimental: true
32
- - os: ubuntu
33
- ruby: head
34
- experimental: true
35
- - os: ubuntu
36
- ruby: 2.6
37
- experimental: false
38
- env: COVERAGE=PartialSummary,Coveralls
39
-
40
- steps:
41
- - uses: actions/checkout@v1
42
- - uses: ruby/setup-ruby@v1
43
- with:
44
- ruby-version: ${{matrix.ruby}}
45
-
46
- - name: Install dependencies
47
- run: ${{matrix.env}} bundle install
48
-
49
- - name: Run tests
50
- timeout-minutes: 5
51
- run: ${{matrix.env}} bundle exec rspec
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
-
11
- # rspec failure tracking
12
- .rspec_status
13
- .covered.db
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- # gem "async", path: "../async"
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
- [![Actions Status](https://github.com/socketry/async-pool/workflows/Development/badge.svg)](https://github.com/socketry/async-pool/actions?workflow=Development)
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/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.25")
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 "bake-bundler"
28
- end