async-pool 0.3.1 → 0.3.6

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: 83fd23243d9177b82bf516eef994a4e3b51eb74cf3427d64f65e98f8002ccfe7
4
- data.tar.gz: 0fb4d07fe7fc10d66e74c7ea718424e6ccf05cf4a533fb700665da584b3e514f
3
+ metadata.gz: 4d128b17bceefb18481cc504c50770ac4ed5954f00d0d1bf2c6081180c73cb67
4
+ data.tar.gz: 4a32a80b5066638e1b0a02a5b7683d6c2a20c13c89d7d42cacd8c30104cf5749
5
5
  SHA512:
6
- metadata.gz: 9af1aae46fc4e6d6981df403b9d94b6c71e8e69f906de2664363b7c158f9e88971d58e5bec05127332de46fb527a9ce45d97c3ec0c33a2af23d424cf63b4ce3a
7
- data.tar.gz: 98f555ac51b15f501ffba41b6868c161520b65a2acc63f052c646fd7968f0f16c7e243063126a026d5b737289342c741fc0b9a1027d8725458b5bf3f44abeb71
6
+ metadata.gz: 9e310c01ee548452e4eeec3cc40bd1b1e3a6e4ec48534bf84648581353fed366cd0a2c8a7acaa872b41d038c441287c5c7154fc3650d47e4f11eb3a462fa2f8c
7
+ data.tar.gz: 0ddbf97821d02ac53a872974f3168603d63148be6c444839d0676a4d73a5bc264246cb2a22814528777924b05529af090d4d3b5571130fe8b090f253abb4ca33
@@ -18,7 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'async/logger'
21
+ require 'console/logger'
22
22
 
23
23
  require 'async'
24
24
  require 'async/notification'
@@ -32,9 +32,13 @@ module Async
32
32
  end
33
33
 
34
34
  def initialize(constructor, limit: nil)
35
+ # All available resources:
35
36
  @resources = {}
36
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.
37
40
  @available = []
41
+
38
42
  @notification = Async::Notification.new
39
43
 
40
44
  @limit = limit
@@ -45,7 +49,7 @@ module Async
45
49
  @gardener = nil
46
50
  end
47
51
 
48
- # @attr [Hash<Resource, Integer>] all allocated resources, and their associated usage.
52
+ # @attribute [Hash(Resource, Integer)] all allocated resources, and their associated usage.
49
53
  attr :resources
50
54
 
51
55
  def size
@@ -66,6 +70,11 @@ module Async
66
70
  return false
67
71
  end
68
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
+
69
78
  # Wait until a pool resource has been freed.
70
79
  def wait
71
80
  @notification.wait
@@ -98,6 +107,8 @@ module Async
98
107
  end
99
108
 
100
109
  def close
110
+ @available.clear
111
+
101
112
  @resources.each_key(&:close)
102
113
  @resources.clear
103
114
 
@@ -119,7 +130,9 @@ module Async
119
130
  unused = []
120
131
 
121
132
  @resources.each do |resource, usage|
122
- unused << resource if usage.zero?
133
+ if usage.zero?
134
+ unused << resource
135
+ end
123
136
  end
124
137
 
125
138
  unused.each do |resource|
@@ -132,11 +145,19 @@ module Async
132
145
  break if @resources.size <= retain
133
146
  end
134
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
+
135
156
  return unused.size
136
157
  end
137
158
 
138
159
  def retire(resource)
139
- Async.logger.debug(self) {"Retire #{resource}"}
160
+ Console.logger.debug(self) {"Retire #{resource}"}
140
161
 
141
162
  @resources.delete(resource)
142
163
 
@@ -150,7 +171,7 @@ module Async
150
171
  def start_gardener
151
172
  return if @gardener
152
173
 
153
- Async(transient: true) do |task|
174
+ Async(transient: true, annotation: "#{self.class} Gardener") do |task|
154
175
  @gardener = task
155
176
 
156
177
  Task.yield
@@ -170,11 +191,42 @@ module Async
170
191
  end.join(";")
171
192
  end
172
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
+
173
209
  def reuse(resource)
174
- Async.logger.debug(self) {"Reuse #{resource}"}
210
+ Console.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
175
228
 
176
- @resources[resource] -= 1
177
- @available.push(resource)
229
+ @resources[resource] = usage - 1
178
230
 
179
231
  @notification.signal
180
232
  end
@@ -185,7 +237,7 @@ module Async
185
237
  @notification.wait
186
238
  end
187
239
 
188
- Async.logger.debug(self) {"Wait for resource -> #{resource}"}
240
+ Console.logger.debug(self) {"Wait for resource -> #{resource}"}
189
241
 
190
242
  # if resource.concurrency > 1
191
243
  # @notification.signal
@@ -194,6 +246,7 @@ module Async
194
246
  return resource
195
247
  end
196
248
 
249
+ # @returns [Object] A new resource in a "used" state.
197
250
  def create_resource
198
251
  self.start_gardener
199
252
 
@@ -201,18 +254,27 @@ module Async
201
254
  if resource = @constructor.call
202
255
  @resources[resource] = 1
203
256
 
204
- @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
205
261
  end
206
262
 
207
263
  return resource
208
264
  end
209
265
 
266
+ # @returns [Object] An existing resource in a "used" state.
210
267
  def available_resource
211
268
  @guard.acquire do
212
269
  while resource = @available.last
213
270
  if usage = @resources[resource] and usage < resource.concurrency
214
271
  if resource.viable?
215
- @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
216
278
 
217
279
  return resource
218
280
  else
@@ -220,12 +282,13 @@ module Async
220
282
  @available.pop
221
283
  end
222
284
  else
285
+ # The resource has been removed already, so skip it and remove it from the availability list.
223
286
  @available.pop
224
287
  end
225
288
  end
226
289
 
227
290
  if @limit.nil? or @resources.size < @limit
228
- Async.logger.debug(self) {"No resources resources, allocating new one..."}
291
+ Console.logger.debug(self) {"No available resources, allocating new one..."}
229
292
 
230
293
  return create_resource
231
294
  end
@@ -18,7 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'async/logger'
21
+ require 'console/logger'
22
22
 
23
23
  require 'async/notification'
24
24
  require 'async/semaphore'
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module Pool
23
- VERSION = "0.3.1"
23
+ VERSION = "0.3.6"
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.1
4
+ version: 0.3.6
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-05-04 00:00:00.000000000 Z
11
+ date: 2021-05-08 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,15 +131,15 @@ 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
  - - ">="
130
138
  - !ruby/object:Gem::Version
131
139
  version: '0'
132
140
  requirements: []
133
- rubygems_version: 3.1.2
134
- signing_key:
141
+ rubygems_version: 3.2.3
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