async-limiter 2.1.0 → 2.2.0
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/limiter/generic.rb +8 -2
- data/lib/async/limiter/limited.rb +31 -14
- data/lib/async/limiter/queued.rb +31 -14
- data/lib/async/limiter/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +15 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d153319af347142e9837f3a8e7523f47f2bbd28d56e497d03c0fd7279302af8f
|
|
4
|
+
data.tar.gz: 9d1da2a75240ba77a68d26141abf50b677d5248b9bf191a50e8821aac3bceee7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a35d89989f3d8faf3cd203a84b2e04125809ae07d6609ce0dc205214f97bc1bf11ab099bbc46542a3fa701674185e641461d548f67bb1d1325b8badce9b6c6cc
|
|
7
|
+
data.tar.gz: df563bd337ae1e9157df61f0b47ae35d1e536b15ad5c0e7cb6fe7ebf48f32bf18249185307aa07d287c3c54df90f8463ce014531763536bd8f92428c0d13e0f9
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
require "async/task"
|
|
8
8
|
require "async/deadline"
|
|
9
|
+
require "async/utilization"
|
|
9
10
|
require "json"
|
|
10
11
|
require_relative "timing/none"
|
|
11
12
|
require_relative "timing/sliding_window"
|
|
@@ -24,10 +25,12 @@ module Async
|
|
|
24
25
|
# Initialize a new generic limiter.
|
|
25
26
|
# @parameter timing [#acquire, #wait, #maximum_cost] Strategy for timing constraints.
|
|
26
27
|
# @parameter parent [Async::Task, nil] Parent task for creating child tasks.
|
|
27
|
-
|
|
28
|
+
# @parameter utilization [#metric] Registry-like object for utilization metrics.
|
|
29
|
+
def initialize(timing: Timing::None, parent: nil, tags: nil, utilization: Async::Utilization::Registry.new)
|
|
28
30
|
@timing = timing
|
|
29
31
|
@parent = parent
|
|
30
32
|
@tags = tags
|
|
33
|
+
@utilization = utilization
|
|
31
34
|
|
|
32
35
|
@mutex = Mutex.new
|
|
33
36
|
end
|
|
@@ -35,6 +38,9 @@ module Async
|
|
|
35
38
|
# @attribute [Array(String)] Tags associated with this limiter for identification or categorization.
|
|
36
39
|
attr :tags
|
|
37
40
|
|
|
41
|
+
# @attribute [#metric] Registry-like object for utilization metrics.
|
|
42
|
+
attr :utilization
|
|
43
|
+
|
|
38
44
|
# @returns [Boolean] Whether this limiter is currently limiting concurrency.
|
|
39
45
|
def limited?
|
|
40
46
|
false
|
|
@@ -67,7 +73,7 @@ module Async
|
|
|
67
73
|
end
|
|
68
74
|
|
|
69
75
|
# Manually acquire a resource with timing and concurrency coordination.
|
|
70
|
-
#
|
|
76
|
+
#
|
|
71
77
|
# This method provides the core acquisition logic with support for:
|
|
72
78
|
# - Flexible timeout handling (blocking, non-blocking, timed)
|
|
73
79
|
# - Cost-based consumption for timing strategies
|
|
@@ -20,18 +20,22 @@ module Async
|
|
|
20
20
|
class Limited < Generic
|
|
21
21
|
# Initialize a limited concurrency limiter.
|
|
22
22
|
# @parameter limit [Integer] Maximum concurrent tasks allowed.
|
|
23
|
-
# @parameter
|
|
24
|
-
# @parameter parent [Async::Task, nil] Parent task for creating child tasks.
|
|
23
|
+
# @parameter options [Hash] Options passed to {Generic#initialize}.
|
|
25
24
|
# @raises [ArgumentError] If limit is not positive.
|
|
26
|
-
def initialize(limit = 1,
|
|
27
|
-
super(
|
|
25
|
+
def initialize(limit = 1, **options)
|
|
26
|
+
super(**options)
|
|
28
27
|
|
|
29
28
|
@limit = limit
|
|
30
29
|
@count = 0
|
|
31
|
-
@waiting_count = 0
|
|
32
|
-
@reacquire_waiting_count = 0
|
|
33
30
|
|
|
34
31
|
@available = ConditionVariable.new
|
|
32
|
+
|
|
33
|
+
@acquired_count_metric = @utilization.metric(:acquired_count)
|
|
34
|
+
@available_count_metric = @utilization.metric(:available_count)
|
|
35
|
+
@waiting_count_metric = @utilization.metric(:waiting_count)
|
|
36
|
+
@reacquire_waiting_count_metric = @utilization.metric(:reacquire_waiting_count)
|
|
37
|
+
|
|
38
|
+
update_utilization_metrics
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
# @attribute [Integer] The maximum number of concurrent tasks.
|
|
@@ -52,12 +56,12 @@ module Async
|
|
|
52
56
|
|
|
53
57
|
# @returns [Integer] Current count of tasks waiting for capacity.
|
|
54
58
|
def waiting_count
|
|
55
|
-
@
|
|
59
|
+
@waiting_count_metric.value
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
# @returns [Integer] Current count of reacquiring tasks waiting for capacity.
|
|
59
63
|
def reacquire_waiting_count
|
|
60
|
-
@
|
|
64
|
+
@reacquire_waiting_count_metric.value
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
# Check if a new task can be acquired.
|
|
@@ -73,6 +77,7 @@ module Async
|
|
|
73
77
|
@mutex.synchronize do
|
|
74
78
|
old_limit = @limit
|
|
75
79
|
@limit = new_limit
|
|
80
|
+
update_utilization_metrics
|
|
76
81
|
|
|
77
82
|
# Wake up waiting tasks if limit increased:
|
|
78
83
|
@available.broadcast if new_limit > old_limit
|
|
@@ -88,8 +93,8 @@ module Async
|
|
|
88
93
|
count: @count,
|
|
89
94
|
acquired_count: @count,
|
|
90
95
|
available_count: @limit - @count,
|
|
91
|
-
waiting_count: @
|
|
92
|
-
reacquire_waiting_count: @
|
|
96
|
+
waiting_count: @waiting_count_metric.value,
|
|
97
|
+
reacquire_waiting_count: @reacquire_waiting_count_metric.value,
|
|
93
98
|
timing: @timing.statistics
|
|
94
99
|
}
|
|
95
100
|
end
|
|
@@ -103,6 +108,7 @@ module Async
|
|
|
103
108
|
return nil if deadline&.expired? && @count >= @limit
|
|
104
109
|
|
|
105
110
|
waiting = false
|
|
111
|
+
acquired = false
|
|
106
112
|
|
|
107
113
|
# Wait for capacity with deadline tracking
|
|
108
114
|
while @count >= @limit
|
|
@@ -110,8 +116,8 @@ module Async
|
|
|
110
116
|
return nil if remaining && remaining <= 0
|
|
111
117
|
|
|
112
118
|
unless waiting
|
|
113
|
-
@
|
|
114
|
-
@
|
|
119
|
+
@waiting_count_metric.increment
|
|
120
|
+
@reacquire_waiting_count_metric.increment if reacquire
|
|
115
121
|
waiting = true
|
|
116
122
|
end
|
|
117
123
|
|
|
@@ -121,22 +127,33 @@ module Async
|
|
|
121
127
|
end
|
|
122
128
|
|
|
123
129
|
@count += 1
|
|
130
|
+
acquired = true
|
|
124
131
|
|
|
125
132
|
return true
|
|
126
133
|
ensure
|
|
127
134
|
if waiting
|
|
128
|
-
@
|
|
129
|
-
@
|
|
135
|
+
@waiting_count_metric.decrement
|
|
136
|
+
@reacquire_waiting_count_metric.decrement if reacquire
|
|
130
137
|
end
|
|
138
|
+
|
|
139
|
+
update_utilization_metrics if acquired
|
|
131
140
|
end
|
|
132
141
|
|
|
133
142
|
# Release resource.
|
|
134
143
|
def release_resource(resource)
|
|
135
144
|
@mutex.synchronize do
|
|
136
145
|
@count -= 1
|
|
146
|
+
update_utilization_metrics
|
|
137
147
|
@available.signal
|
|
138
148
|
end
|
|
139
149
|
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def update_utilization_metrics
|
|
154
|
+
@acquired_count_metric.set(@count)
|
|
155
|
+
@available_count_metric.set(@limit - @count)
|
|
156
|
+
end
|
|
140
157
|
end
|
|
141
158
|
end
|
|
142
159
|
end
|
data/lib/async/limiter/queued.rb
CHANGED
|
@@ -28,13 +28,17 @@ module Async
|
|
|
28
28
|
|
|
29
29
|
# Initialize a queue-based limiter.
|
|
30
30
|
# @parameter queue [#push, #pop, #empty?] Thread-safe queue containing pre-existing resources.
|
|
31
|
-
# @parameter
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
super(timing: timing, parent: parent)
|
|
31
|
+
# @parameter options [Hash] Options passed to {Generic#initialize}.
|
|
32
|
+
def initialize(queue = self.class.default_queue, **options)
|
|
33
|
+
super(**options)
|
|
35
34
|
@queue = queue
|
|
36
|
-
|
|
37
|
-
@
|
|
35
|
+
|
|
36
|
+
@acquired_count_metric = @utilization.metric(:acquired_count)
|
|
37
|
+
@available_count_metric = @utilization.metric(:available_count)
|
|
38
|
+
@waiting_count_metric = @utilization.metric(:waiting_count)
|
|
39
|
+
@reacquire_waiting_count_metric = @utilization.metric(:reacquire_waiting_count)
|
|
40
|
+
|
|
41
|
+
update_utilization_metrics
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
# @attribute [Queue] The queue managing resources.
|
|
@@ -42,7 +46,7 @@ module Async
|
|
|
42
46
|
|
|
43
47
|
# @returns [Integer] Current count of acquired resources.
|
|
44
48
|
def acquired_count
|
|
45
|
-
@
|
|
49
|
+
@acquired_count_metric.value
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
# @returns [Integer] Current count of available resources.
|
|
@@ -57,7 +61,7 @@ module Async
|
|
|
57
61
|
|
|
58
62
|
# @returns [Integer] Current count of reacquiring tasks waiting for resources.
|
|
59
63
|
def reacquire_waiting_count
|
|
60
|
-
@
|
|
64
|
+
@reacquire_waiting_count_metric.value
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
# Check if a new task can be acquired.
|
|
@@ -73,6 +77,8 @@ module Async
|
|
|
73
77
|
count.times do
|
|
74
78
|
@queue.push(value)
|
|
75
79
|
end
|
|
80
|
+
|
|
81
|
+
update_utilization_metrics
|
|
76
82
|
end
|
|
77
83
|
|
|
78
84
|
# Get current limiter statistics.
|
|
@@ -82,10 +88,10 @@ module Async
|
|
|
82
88
|
{
|
|
83
89
|
waiting: @queue.waiting_count,
|
|
84
90
|
available: @queue.size,
|
|
85
|
-
acquired_count: @
|
|
91
|
+
acquired_count: @acquired_count_metric.value,
|
|
86
92
|
available_count: @queue.size,
|
|
87
93
|
waiting_count: @queue.waiting_count,
|
|
88
|
-
reacquire_waiting_count: @
|
|
94
|
+
reacquire_waiting_count: @reacquire_waiting_count_metric.value,
|
|
89
95
|
timing: @timing.statistics
|
|
90
96
|
}
|
|
91
97
|
end
|
|
@@ -95,25 +101,36 @@ module Async
|
|
|
95
101
|
|
|
96
102
|
# Acquire a resource from the queue with optional deadline.
|
|
97
103
|
def acquire_resource(deadline, reacquire: false, **options)
|
|
98
|
-
@
|
|
104
|
+
@reacquire_waiting_count_metric.increment if reacquire
|
|
105
|
+
update_utilization_metrics if reacquire
|
|
99
106
|
|
|
100
107
|
@mutex.unlock
|
|
101
108
|
resource = @queue.pop(timeout: deadline&.remaining, **options)
|
|
102
109
|
return resource
|
|
103
110
|
ensure
|
|
104
111
|
@mutex.lock
|
|
105
|
-
@
|
|
106
|
-
@
|
|
112
|
+
@reacquire_waiting_count_metric.decrement if reacquire
|
|
113
|
+
@acquired_count_metric.increment if resource
|
|
114
|
+
update_utilization_metrics if reacquire || resource
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
# Release a previously acquired resource back to the queue.
|
|
110
118
|
def release_resource(value)
|
|
111
119
|
@mutex.synchronize do
|
|
112
|
-
@
|
|
120
|
+
@acquired_count_metric.decrement if @acquired_count_metric.value > 0
|
|
121
|
+
update_utilization_metrics
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
# Return a default resource to the queue:
|
|
116
125
|
@queue.push(value)
|
|
126
|
+
update_utilization_metrics
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def update_utilization_metrics
|
|
132
|
+
@available_count_metric.set(@queue.size)
|
|
133
|
+
@waiting_count_metric.set(@queue.waiting_count)
|
|
117
134
|
end
|
|
118
135
|
end
|
|
119
136
|
end
|
data/readme.md
CHANGED
|
@@ -24,6 +24,10 @@ Please see the [project documentation](https://socketry.github.io/async-limiter/
|
|
|
24
24
|
|
|
25
25
|
Please see the [project releases](https://socketry.github.io/async-limiter/releases/index) for all releases.
|
|
26
26
|
|
|
27
|
+
### v2.2.0
|
|
28
|
+
|
|
29
|
+
- Add `async-utilization` metrics for limiter telemetry counters.
|
|
30
|
+
|
|
27
31
|
### v2.1.0
|
|
28
32
|
|
|
29
33
|
- Add telemetry counters to `Async::Limiter::Limited` and `Async::Limiter::Queued`: `acquired_count`, `available_count`, `waiting_count`, and `reacquire_waiting_count` for observability into limiter state.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v2.2.0
|
|
4
|
+
|
|
5
|
+
- Add `async-utilization` metrics for limiter telemetry counters.
|
|
6
|
+
|
|
3
7
|
## v2.1.0
|
|
4
8
|
|
|
5
9
|
- Add telemetry counters to `Async::Limiter::Limited` and `Async::Limiter::Queued`: `acquired_count`, `available_count`, `waiting_count`, and `reacquire_waiting_count` for observability into limiter state.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-limiter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bruno Sutic
|
|
@@ -56,6 +56,20 @@ dependencies:
|
|
|
56
56
|
- - ">="
|
|
57
57
|
- !ruby/object:Gem::Version
|
|
58
58
|
version: '2.31'
|
|
59
|
+
- !ruby/object:Gem::Dependency
|
|
60
|
+
name: async-utilization
|
|
61
|
+
requirement: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - "~>"
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0.4'
|
|
66
|
+
type: :runtime
|
|
67
|
+
prerelease: false
|
|
68
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - "~>"
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0.4'
|
|
59
73
|
executables: []
|
|
60
74
|
extensions: []
|
|
61
75
|
extra_rdoc_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|