async-utilization 0.1.0 → 0.3.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/utilization/metric.rb +20 -12
- data/lib/async/utilization/registry.rb +18 -10
- data/lib/async/utilization/version.rb +1 -1
- data/lib/async/utilization.rb +1 -34
- data/readme.md +4 -0
- data/releases.md +4 -0
- data/test/async/utilization/metric.rb +77 -34
- data/test/async/utilization/registry.rb +4 -4
- data.tar.gz.sig +0 -0
- metadata +2 -2
- 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: 10d5277f8a02e1f72a8466b86dae5245d8beacf60f7310b5f4b2fbbe829ad2b2
|
|
4
|
+
data.tar.gz: 8e1519b41206468ffcd4c5197aecd0843882e5c637db9db24fce67f87fd34386
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aff8c89a734ee90bb716186f4a9133373bc222c26a0b4bb089152a9bce6bd2d76105a91a6c99f3a5333be4afef83ba3c88637e64b69a2629ab185e94b6035a4a
|
|
7
|
+
data.tar.gz: 853481053488156d6baa7be3cff2f7c19e9bad60028e44649f96bfc978c07c173065bb0d8bd17105ae1248204d569bc115fcc0e146ce3ee25886b8efcc2580e9
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -43,30 +43,38 @@ module Async
|
|
|
43
43
|
@cached_buffer = nil
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
# Increment the metric value
|
|
46
|
+
# Increment the metric value.
|
|
47
47
|
#
|
|
48
48
|
# Uses the fast path (direct buffer write) when cache is valid and observer is available.
|
|
49
49
|
#
|
|
50
|
-
# @yield Optional block - if provided, decrements the field after the block completes.
|
|
51
50
|
# @returns [Integer] The new value of the field.
|
|
52
|
-
def increment
|
|
51
|
+
def increment
|
|
53
52
|
@guard.synchronize do
|
|
54
53
|
@value += 1
|
|
55
54
|
write_direct(@value)
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
if block_given?
|
|
59
|
-
begin
|
|
60
|
-
yield
|
|
61
|
-
ensure
|
|
62
|
-
# Decrement after block completes
|
|
63
|
-
decrement
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
57
|
@value
|
|
68
58
|
end
|
|
69
59
|
|
|
60
|
+
# Track an operation: increment before the block, decrement after it completes.
|
|
61
|
+
#
|
|
62
|
+
# Returns the block's return value. Use for active/count metrics that should
|
|
63
|
+
# reflect the number of operations currently in progress.
|
|
64
|
+
#
|
|
65
|
+
# @yield The operation to track.
|
|
66
|
+
# @returns [Object] The block's return value.
|
|
67
|
+
def track(&block)
|
|
68
|
+
raise ArgumentError, "block required" unless block_given?
|
|
69
|
+
|
|
70
|
+
increment
|
|
71
|
+
begin
|
|
72
|
+
yield
|
|
73
|
+
ensure
|
|
74
|
+
decrement
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
70
78
|
# Decrement the metric value.
|
|
71
79
|
#
|
|
72
80
|
# Uses the fast path (direct buffer write) when cache is valid and observer is available.
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require "thread/local"
|
|
7
|
-
|
|
8
6
|
module Async
|
|
9
7
|
module Utilization
|
|
10
8
|
# Registry for emitting utilization metrics.
|
|
@@ -12,8 +10,9 @@ module Async
|
|
|
12
10
|
# The registry tracks values directly and notifies a registered observer
|
|
13
11
|
# when values change. The observer (like Observer) can write to its backend.
|
|
14
12
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
13
|
+
# Registries should be explicitly created and passed to components that need them.
|
|
14
|
+
# In service contexts, registries are typically created via the evaluator and
|
|
15
|
+
# shared across components within the same service instance.
|
|
17
16
|
#
|
|
18
17
|
# When an observer is added, it is immediately notified of all current values
|
|
19
18
|
# so it can sync its state. When values change, the observer is notified.
|
|
@@ -23,7 +22,7 @@ module Async
|
|
|
23
22
|
#
|
|
24
23
|
# # Emit metrics - values tracked in registry
|
|
25
24
|
# registry.increment(:total_requests)
|
|
26
|
-
# registry.
|
|
25
|
+
# registry.track(:active_requests) do
|
|
27
26
|
# # Handle request - auto-decrements when block completes
|
|
28
27
|
# end
|
|
29
28
|
#
|
|
@@ -36,7 +35,6 @@ module Async
|
|
|
36
35
|
# observer = Async::Utilization::Observer.open(schema, "/path/to/shm", 4096, 0)
|
|
37
36
|
# registry.observer = observer
|
|
38
37
|
class Registry
|
|
39
|
-
extend Thread::Local
|
|
40
38
|
|
|
41
39
|
# Initialize a new registry.
|
|
42
40
|
def initialize
|
|
@@ -95,15 +93,25 @@ module Async
|
|
|
95
93
|
metric(field).set(value)
|
|
96
94
|
end
|
|
97
95
|
|
|
98
|
-
# Increment a field value
|
|
96
|
+
# Increment a field value.
|
|
99
97
|
#
|
|
100
98
|
# Delegates to the metric instance for the given field.
|
|
101
99
|
#
|
|
102
100
|
# @parameter field [Symbol] The field name to increment.
|
|
103
|
-
# @yield Optional block - if provided, decrements the field after the block completes.
|
|
104
101
|
# @returns [Integer] The new value of the field.
|
|
105
|
-
def increment(field
|
|
106
|
-
metric(field).increment
|
|
102
|
+
def increment(field)
|
|
103
|
+
metric(field).increment
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Track an operation: increment before the block, decrement after it completes.
|
|
107
|
+
#
|
|
108
|
+
# Delegates to the metric instance for the given field.
|
|
109
|
+
#
|
|
110
|
+
# @parameter field [Symbol] The field name to track.
|
|
111
|
+
# @yield The operation to track.
|
|
112
|
+
# @returns [Object] The block's return value.
|
|
113
|
+
def track(field, &block)
|
|
114
|
+
metric(field).track(&block)
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
# Decrement a field value.
|
data/lib/async/utilization.rb
CHANGED
|
@@ -15,42 +15,9 @@ module Async
|
|
|
15
15
|
#
|
|
16
16
|
# This module provides a convenient interface for tracking utilization metrics
|
|
17
17
|
# that can be synchronized to shared memory for inter-process communication.
|
|
18
|
-
#
|
|
19
|
-
# thread-local behavior.
|
|
18
|
+
# Registries should be explicitly created and passed to components that need them.
|
|
20
19
|
#
|
|
21
20
|
# See the {file:guides/getting-started/readme.md Getting Started} guide for usage examples.
|
|
22
21
|
module Utilization
|
|
23
|
-
# Set the observer for utilization metrics.
|
|
24
|
-
#
|
|
25
|
-
# When an observer is set, it is notified of all current metric values
|
|
26
|
-
# so it can sync its state. The observer must implement `set(field, value)`.
|
|
27
|
-
#
|
|
28
|
-
# Delegates to the thread-local {Registry} instance.
|
|
29
|
-
#
|
|
30
|
-
# @parameter observer [#set] The observer to set.
|
|
31
|
-
def self.observer=(observer)
|
|
32
|
-
Registry.instance.observer = observer
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Get a cached metric reference for a field.
|
|
36
|
-
#
|
|
37
|
-
# Returns a {Metric} instance that caches all details needed for fast writes
|
|
38
|
-
# to shared memory, avoiding hash lookups on the fast path.
|
|
39
|
-
#
|
|
40
|
-
# This is the recommended way to access metrics for optimal performance.
|
|
41
|
-
#
|
|
42
|
-
# Delegates to the thread-local {Registry} instance.
|
|
43
|
-
#
|
|
44
|
-
# @parameter field [Symbol] The field name to get a metric for.
|
|
45
|
-
# @returns [Metric] A metric instance for the given field.
|
|
46
|
-
# @example Get a metric and increment it:
|
|
47
|
-
# current_requests = Async::Utilization.metric(:current_requests)
|
|
48
|
-
# current_requests.increment
|
|
49
|
-
# current_requests.increment do
|
|
50
|
-
# # Handle request - auto-decrements when block completes
|
|
51
|
-
# end
|
|
52
|
-
def self.metric(field)
|
|
53
|
-
Registry.instance.metric(field)
|
|
54
|
-
end
|
|
55
22
|
end
|
|
56
23
|
end
|
data/readme.md
CHANGED
|
@@ -14,6 +14,10 @@ Please see the [project documentation](https://socketry.github.io/async-utilizat
|
|
|
14
14
|
|
|
15
15
|
Please see the [project releases](https://socketry.github.io/async-utilization/releases/index) for all releases.
|
|
16
16
|
|
|
17
|
+
### v0.3.0
|
|
18
|
+
|
|
19
|
+
- Introduce `Metric#track{...}` for increment -\> decrement.
|
|
20
|
+
|
|
17
21
|
### v0.1.0
|
|
18
22
|
|
|
19
23
|
- Initial implementation.
|
data/releases.md
CHANGED
|
@@ -29,28 +29,24 @@ describe Async::Utilization::Metric do
|
|
|
29
29
|
Async::Utilization::Observer.open(schema, shm_path, segment_size, offset)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
let(:registry) {Async::Utilization::Registry.new}
|
|
33
|
+
|
|
32
34
|
before do
|
|
33
35
|
File.open(shm_path, "w+b") do |file|
|
|
34
36
|
file.truncate(file_size)
|
|
35
37
|
end
|
|
36
|
-
|
|
37
|
-
# Reset the registry to ensure clean state between tests
|
|
38
|
-
registry = Async::Utilization::Registry.instance
|
|
39
|
-
registry.instance_variable_set(:@values, Hash.new(0))
|
|
40
|
-
registry.instance_variable_set(:@metrics, {})
|
|
41
|
-
registry.instance_variable_set(:@observer, nil)
|
|
42
38
|
end
|
|
43
39
|
|
|
44
40
|
it "can create a metric from a field name" do
|
|
45
|
-
metric =
|
|
41
|
+
metric = registry.metric(:total_requests)
|
|
46
42
|
|
|
47
43
|
expect(metric).to be_a(Async::Utilization::Metric)
|
|
48
44
|
expect(metric.name).to be == :total_requests
|
|
49
45
|
end
|
|
50
46
|
|
|
51
47
|
it "can increment a metric" do
|
|
52
|
-
|
|
53
|
-
metric =
|
|
48
|
+
registry.observer = observer
|
|
49
|
+
metric = registry.metric(:total_requests)
|
|
54
50
|
|
|
55
51
|
value = metric.increment
|
|
56
52
|
expect(value).to be == 1
|
|
@@ -62,8 +58,8 @@ describe Async::Utilization::Metric do
|
|
|
62
58
|
end
|
|
63
59
|
|
|
64
60
|
it "can decrement a metric" do
|
|
65
|
-
|
|
66
|
-
metric =
|
|
61
|
+
registry.observer = observer
|
|
62
|
+
metric = registry.metric(:total_requests)
|
|
67
63
|
|
|
68
64
|
metric.increment
|
|
69
65
|
metric.increment
|
|
@@ -74,8 +70,8 @@ describe Async::Utilization::Metric do
|
|
|
74
70
|
end
|
|
75
71
|
|
|
76
72
|
it "can set a metric value" do
|
|
77
|
-
|
|
78
|
-
metric =
|
|
73
|
+
registry.observer = observer
|
|
74
|
+
metric = registry.metric(:total_requests)
|
|
79
75
|
|
|
80
76
|
metric.set(42)
|
|
81
77
|
expect(metric.value).to be == 42
|
|
@@ -84,23 +80,62 @@ describe Async::Utilization::Metric do
|
|
|
84
80
|
expect(metric.value).to be == 100
|
|
85
81
|
end
|
|
86
82
|
|
|
87
|
-
it "can
|
|
88
|
-
|
|
89
|
-
metric =
|
|
83
|
+
it "can track an operation with auto-decrement" do
|
|
84
|
+
registry.observer = observer
|
|
85
|
+
metric = registry.metric(:active_requests)
|
|
90
86
|
|
|
91
|
-
metric.
|
|
87
|
+
metric.track do
|
|
92
88
|
expect(metric.value).to be == 1
|
|
93
89
|
end
|
|
94
90
|
|
|
95
91
|
expect(metric.value).to be == 0
|
|
96
92
|
end
|
|
97
93
|
|
|
98
|
-
it "
|
|
99
|
-
|
|
100
|
-
metric =
|
|
94
|
+
it "returns the metric value when increment is called" do
|
|
95
|
+
registry.observer = observer
|
|
96
|
+
metric = registry.metric(:total_requests)
|
|
97
|
+
|
|
98
|
+
result = metric.increment
|
|
99
|
+
expect(result).to be == 1
|
|
100
|
+
expect(result).to be == metric.value
|
|
101
|
+
|
|
102
|
+
result = metric.increment
|
|
103
|
+
expect(result).to be == 2
|
|
104
|
+
expect(result).to be == metric.value
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "returns the block's return value when track is called" do
|
|
108
|
+
registry.observer = observer
|
|
109
|
+
metric = registry.metric(:active_requests)
|
|
110
|
+
|
|
111
|
+
# Block returns a string
|
|
112
|
+
result = metric.track do
|
|
113
|
+
"connection_object"
|
|
114
|
+
end
|
|
115
|
+
expect(result).to be == "connection_object"
|
|
116
|
+
expect(metric.value).to be == 0 # Should be decremented after block
|
|
117
|
+
|
|
118
|
+
# Block returns an integer
|
|
119
|
+
result = metric.track do
|
|
120
|
+
42
|
|
121
|
+
end
|
|
122
|
+
expect(result).to be == 42
|
|
123
|
+
expect(metric.value).to be == 0 # Should be decremented after block
|
|
124
|
+
|
|
125
|
+
# Block returns nil
|
|
126
|
+
result = metric.track do
|
|
127
|
+
nil
|
|
128
|
+
end
|
|
129
|
+
expect(result).to be == nil
|
|
130
|
+
expect(metric.value).to be == 0 # Should be decremented after block
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "decrements even if track block raises an error" do
|
|
134
|
+
registry.observer = observer
|
|
135
|
+
metric = registry.metric(:active_requests)
|
|
101
136
|
|
|
102
137
|
begin
|
|
103
|
-
metric.
|
|
138
|
+
metric.track do
|
|
104
139
|
raise "Test error"
|
|
105
140
|
end
|
|
106
141
|
rescue => error
|
|
@@ -110,9 +145,17 @@ describe Async::Utilization::Metric do
|
|
|
110
145
|
expect(metric.value).to be == 0
|
|
111
146
|
end
|
|
112
147
|
|
|
148
|
+
it "raises ArgumentError when track is called without a block" do
|
|
149
|
+
metric = registry.metric(:active_requests)
|
|
150
|
+
|
|
151
|
+
expect do
|
|
152
|
+
metric.track
|
|
153
|
+
end.to raise_exception(ArgumentError, message: be == "block required")
|
|
154
|
+
end
|
|
155
|
+
|
|
113
156
|
it "writes directly to shared memory when observer is set" do
|
|
114
|
-
|
|
115
|
-
metric =
|
|
157
|
+
registry.observer = observer
|
|
158
|
+
metric = registry.metric(:total_requests)
|
|
116
159
|
|
|
117
160
|
metric.set(42)
|
|
118
161
|
|
|
@@ -122,8 +165,8 @@ describe Async::Utilization::Metric do
|
|
|
122
165
|
end
|
|
123
166
|
|
|
124
167
|
it "invalidates cache when observer changes" do
|
|
125
|
-
|
|
126
|
-
metric =
|
|
168
|
+
registry.observer = observer
|
|
169
|
+
metric = registry.metric(:total_requests)
|
|
127
170
|
|
|
128
171
|
# Set a value - cache should be built
|
|
129
172
|
metric.set(10)
|
|
@@ -139,7 +182,7 @@ describe Async::Utilization::Metric do
|
|
|
139
182
|
new_observer = Async::Utilization::Observer.open(new_schema, new_shm_path, segment_size, 0)
|
|
140
183
|
|
|
141
184
|
# Change observer - cache should be invalidated
|
|
142
|
-
|
|
185
|
+
registry.observer = new_observer
|
|
143
186
|
|
|
144
187
|
# Set a new value - cache should be rebuilt
|
|
145
188
|
metric.set(20)
|
|
@@ -151,7 +194,7 @@ describe Async::Utilization::Metric do
|
|
|
151
194
|
end
|
|
152
195
|
|
|
153
196
|
it "works without an observer" do
|
|
154
|
-
metric =
|
|
197
|
+
metric = registry.metric(:total_requests)
|
|
155
198
|
|
|
156
199
|
# Should work fine without observer (uses fallback path)
|
|
157
200
|
metric.increment
|
|
@@ -161,21 +204,21 @@ describe Async::Utilization::Metric do
|
|
|
161
204
|
expect(metric.value).to be == 5
|
|
162
205
|
|
|
163
206
|
# Set observer and verify it works with fast path
|
|
164
|
-
|
|
207
|
+
registry.observer = observer
|
|
165
208
|
metric.set(10)
|
|
166
209
|
expect(metric.value).to be == 10
|
|
167
210
|
end
|
|
168
211
|
|
|
169
212
|
it "returns the same metric instance for the same field" do
|
|
170
|
-
metric1 =
|
|
171
|
-
metric2 =
|
|
213
|
+
metric1 = registry.metric(:total_requests)
|
|
214
|
+
metric2 = registry.metric(:total_requests)
|
|
172
215
|
|
|
173
216
|
expect(metric1).to be == metric2
|
|
174
217
|
end
|
|
175
218
|
|
|
176
219
|
it "falls back to observer.set when write_direct fails" do
|
|
177
|
-
|
|
178
|
-
metric =
|
|
220
|
+
registry.observer = observer
|
|
221
|
+
metric = registry.metric(:total_requests)
|
|
179
222
|
|
|
180
223
|
# Force cache to be invalid by invalidating it
|
|
181
224
|
metric.invalidate
|
|
@@ -190,8 +233,8 @@ describe Async::Utilization::Metric do
|
|
|
190
233
|
end
|
|
191
234
|
|
|
192
235
|
it "handles write errors gracefully" do
|
|
193
|
-
|
|
194
|
-
metric =
|
|
236
|
+
registry.observer = observer
|
|
237
|
+
metric = registry.metric(:total_requests)
|
|
195
238
|
|
|
196
239
|
# Set a value first to build the cache
|
|
197
240
|
metric.set(10)
|
|
@@ -37,17 +37,17 @@ describe Async::Utilization::Registry do
|
|
|
37
37
|
expect(registry.values[:test_field]).to be == 42
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
it "can auto-decrement
|
|
41
|
-
registry.
|
|
40
|
+
it "can track an operation with auto-decrement" do
|
|
41
|
+
registry.track(:test_field) do
|
|
42
42
|
expect(registry.values[:test_field]).to be == 1
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
expect(registry.values[:test_field]).to be == 0
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
it "decrements even if block raises an error" do
|
|
48
|
+
it "decrements even if track block raises an error" do
|
|
49
49
|
begin
|
|
50
|
-
registry.
|
|
50
|
+
registry.track(:test_field) do
|
|
51
51
|
raise "Error!"
|
|
52
52
|
end
|
|
53
53
|
rescue
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-utilization
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -97,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
97
97
|
requirements:
|
|
98
98
|
- - ">="
|
|
99
99
|
- !ruby/object:Gem::Version
|
|
100
|
-
version: '3.
|
|
100
|
+
version: '3.3'
|
|
101
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
102
|
requirements:
|
|
103
103
|
- - ">="
|
metadata.gz.sig
CHANGED
|
Binary file
|