async-utilization 0.3.1 → 0.3.2
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 +40 -40
- data/lib/async/utilization/observer.rb +0 -16
- data/lib/async/utilization/registry.rb +12 -13
- data/lib/async/utilization/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +4 -0
- data/test/async/utilization/metric.rb +25 -25
- data/test/async/utilization/observer.rb +9 -49
- data/test/async/utilization/registry.rb +33 -30
- data.tar.gz.sig +0 -0
- metadata +1 -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: 2494a3f35fcbfd987fe6520ceda64225ccc6592ebe4072f0260f38a430d0d3fc
|
|
4
|
+
data.tar.gz: '0668665f31e13ac0d00c5eded51ad94f7f394927318e05ef83f7a5cd818e22e3'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 863148a136e0a8696c337c5c24e18f8751bea2b586f1a703235992103c5454d1621d9296c401ded2d75aa532255beab3ae7ee2ad84211a8eb3b9971aac7b5fc8
|
|
7
|
+
data.tar.gz: 4a6e0ee78558c67dd2f8c5e8bb284c0aaed26cb23a3df700b451841a8081dafd9973ff2ef033f7f137cae4e657b60eae29cfeca89a8edd7ca3233f46a7318ce5
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -11,16 +11,26 @@ module Async
|
|
|
11
11
|
# including the buffer, offset, and type. When the observer changes, the cache
|
|
12
12
|
# is invalidated and rebuilt on the next access.
|
|
13
13
|
class Metric
|
|
14
|
+
# Create a new metric for the given field and observer.
|
|
15
|
+
#
|
|
16
|
+
# @parameter field [Symbol] The field name for this metric.
|
|
17
|
+
# @parameter observer [Observer | Nil] The observer to associate with this metric.
|
|
18
|
+
# @returns [Metric] A new metric instance.
|
|
19
|
+
def self.for(field, observer)
|
|
20
|
+
self.new(field).tap do |metric|
|
|
21
|
+
metric.observer = observer
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
14
25
|
# Initialize a new metric.
|
|
15
26
|
#
|
|
16
27
|
# @parameter name [Symbol] The field name for this metric.
|
|
17
|
-
|
|
18
|
-
def initialize(name, registry)
|
|
28
|
+
def initialize(name)
|
|
19
29
|
@name = name.to_sym
|
|
20
|
-
@registry = registry
|
|
21
30
|
@value = 0
|
|
22
|
-
|
|
23
|
-
@
|
|
31
|
+
|
|
32
|
+
@observer = nil
|
|
33
|
+
@cached_field = nil
|
|
24
34
|
@cached_buffer = nil
|
|
25
35
|
@guard = Mutex.new
|
|
26
36
|
end
|
|
@@ -34,19 +44,35 @@ module Async
|
|
|
34
44
|
# @attribute [Mutex] The mutex for thread safety.
|
|
35
45
|
attr :guard
|
|
36
46
|
|
|
37
|
-
#
|
|
47
|
+
# Set the observer and rebuild cache.
|
|
38
48
|
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
# This is called when the registry assigns a new observer (or removes it).
|
|
50
|
+
# The cache is invalidated and then immediately recomputed so that the
|
|
51
|
+
# fast write path doesn't need to re-check the observer on the first write.
|
|
52
|
+
#
|
|
53
|
+
# @parameter observer [Observer | Nil] The new observer (or nil).
|
|
54
|
+
def observer=(observer)
|
|
55
|
+
@guard.synchronize do
|
|
56
|
+
@observer = observer
|
|
57
|
+
|
|
58
|
+
if @observer
|
|
59
|
+
if field = @observer.schema[@name]
|
|
60
|
+
if buffer = @observer.buffer
|
|
61
|
+
@cached_field = field
|
|
62
|
+
@cached_buffer = buffer
|
|
63
|
+
|
|
64
|
+
return write_direct(@value)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
@cached_field = nil
|
|
70
|
+
@cached_buffer = nil
|
|
71
|
+
end
|
|
44
72
|
end
|
|
45
73
|
|
|
46
74
|
# Increment the metric value.
|
|
47
75
|
#
|
|
48
|
-
# Uses the fast path (direct buffer write) when cache is valid and observer is available.
|
|
49
|
-
#
|
|
50
76
|
# @returns [Integer] The new value of the field.
|
|
51
77
|
def increment
|
|
52
78
|
@guard.synchronize do
|
|
@@ -103,28 +129,6 @@ module Async
|
|
|
103
129
|
|
|
104
130
|
protected
|
|
105
131
|
|
|
106
|
-
# Check if the cache is valid and rebuild if necessary.
|
|
107
|
-
#
|
|
108
|
-
# Always attempts to build the cache if it's invalid. Returns true if cache
|
|
109
|
-
# is now valid (observer exists, field is in schema, and buffer is available), false otherwise.
|
|
110
|
-
#
|
|
111
|
-
# @returns [bool] True if cache is valid, false otherwise.
|
|
112
|
-
def ensure_cache_valid!
|
|
113
|
-
unless @cache_valid
|
|
114
|
-
if observer = @registry.observer
|
|
115
|
-
if field = observer.schema[@name]
|
|
116
|
-
if buffer = observer.buffer
|
|
117
|
-
@cached_field_info = field
|
|
118
|
-
@cached_buffer = buffer
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# Once we've validated the cache, even if there was no observer or buffer, we mark it as valid, so that we don't try to revalidate it again:
|
|
124
|
-
@cache_valid = true
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
132
|
# Write directly to the cached buffer if available.
|
|
129
133
|
#
|
|
130
134
|
# This is the fast path that avoids hash lookups. Always ensures cache is valid
|
|
@@ -133,16 +137,12 @@ module Async
|
|
|
133
137
|
# @parameter value [Numeric] The value to write.
|
|
134
138
|
# @returns [Boolean] Whether the write succeeded.
|
|
135
139
|
def write_direct(value)
|
|
136
|
-
self.ensure_cache_valid!
|
|
137
|
-
|
|
138
140
|
if @cached_buffer
|
|
139
|
-
@cached_buffer.set_value(@
|
|
141
|
+
@cached_buffer.set_value(@cached_field.type, @cached_field.offset, value)
|
|
140
142
|
end
|
|
141
143
|
|
|
142
144
|
return true
|
|
143
145
|
rescue => error
|
|
144
|
-
# If write fails, log warning but don't invalidate cache
|
|
145
|
-
# The error might be transient, and invalidating would force hash lookups
|
|
146
146
|
Console.warn(self, "Failed to write metric value!", metric: {name: @name, value: value}, exception: error)
|
|
147
147
|
|
|
148
148
|
return false
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require "console"
|
|
7
6
|
require_relative "schema"
|
|
8
7
|
|
|
9
8
|
module Async
|
|
@@ -71,21 +70,6 @@ module Async
|
|
|
71
70
|
|
|
72
71
|
# @attribute [IO::Buffer] The mapped buffer for shared memory.
|
|
73
72
|
attr :buffer
|
|
74
|
-
|
|
75
|
-
# Set a field value.
|
|
76
|
-
#
|
|
77
|
-
# Writes the value to shared memory at the offset defined by the schema.
|
|
78
|
-
# Only fields defined in the schema will be written.
|
|
79
|
-
#
|
|
80
|
-
# @parameter field [Symbol] The field name to set.
|
|
81
|
-
# @parameter value [Numeric] The value to set.
|
|
82
|
-
def set(field, value)
|
|
83
|
-
if field = @schema[field]
|
|
84
|
-
@buffer.set_value(field.type, field.offset, value)
|
|
85
|
-
end
|
|
86
|
-
rescue => error
|
|
87
|
-
Console.warn(self, "Failed to set field in shared memory!", field: field, exception: error)
|
|
88
|
-
end
|
|
89
73
|
end
|
|
90
74
|
end
|
|
91
75
|
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
+
require "console"
|
|
7
|
+
|
|
6
8
|
module Async
|
|
7
9
|
module Utilization
|
|
8
10
|
# Registry for emitting utilization metrics.
|
|
@@ -61,26 +63,23 @@ module Async
|
|
|
61
63
|
|
|
62
64
|
# Set the observer for the registry.
|
|
63
65
|
#
|
|
64
|
-
# When an observer is set,
|
|
65
|
-
#
|
|
66
|
-
#
|
|
66
|
+
# When an observer is set, all cached metrics are updated so they write
|
|
67
|
+
# directly to the observer's buffer. The observer must expose `schema`
|
|
68
|
+
# and `buffer` attributes.
|
|
67
69
|
#
|
|
68
|
-
# @parameter observer [
|
|
70
|
+
# @parameter observer [Observer | Nil] The observer to set.
|
|
69
71
|
def observer=(observer)
|
|
70
72
|
@guard.synchronize do
|
|
71
|
-
|
|
73
|
+
@observer = observer
|
|
74
|
+
|
|
75
|
+
# Invalidate all cached metrics with new observer (or nil)
|
|
72
76
|
@metrics.each_value do |metric|
|
|
73
|
-
metric.
|
|
77
|
+
metric.observer = observer
|
|
74
78
|
end
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
# Console.info(self, "Observer assigned", observer: observer, metric_count: @metrics.size)
|
|
77
81
|
end
|
|
78
82
|
|
|
79
|
-
# Notify observer of all current metric values (outside guard to avoid deadlock)
|
|
80
|
-
@metrics.each do |name, metric|
|
|
81
|
-
value = metric.guard.synchronize{metric.value}
|
|
82
|
-
observer.set(name, value)
|
|
83
|
-
end
|
|
84
83
|
end
|
|
85
84
|
|
|
86
85
|
# Set a field value.
|
|
@@ -135,7 +134,7 @@ module Async
|
|
|
135
134
|
field = field.to_sym
|
|
136
135
|
|
|
137
136
|
@guard.synchronize do
|
|
138
|
-
@metrics[field] ||= Metric.
|
|
137
|
+
@metrics[field] ||= Metric.for(field, @observer)
|
|
139
138
|
end
|
|
140
139
|
end
|
|
141
140
|
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.2
|
|
18
|
+
|
|
19
|
+
- Better observer state handling.
|
|
20
|
+
|
|
17
21
|
### v0.3.1
|
|
18
22
|
|
|
19
23
|
- Remove unused `thread-local` dependency.
|
data/releases.md
CHANGED
|
@@ -154,8 +154,8 @@ describe Async::Utilization::Metric do
|
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
it "writes directly to shared memory when observer is set" do
|
|
157
|
-
registry.observer = observer
|
|
158
157
|
metric = registry.metric(:total_requests)
|
|
158
|
+
registry.observer = observer
|
|
159
159
|
|
|
160
160
|
metric.set(42)
|
|
161
161
|
|
|
@@ -216,22 +216,6 @@ describe Async::Utilization::Metric do
|
|
|
216
216
|
expect(metric1).to be == metric2
|
|
217
217
|
end
|
|
218
218
|
|
|
219
|
-
it "falls back to observer.set when write_direct fails" do
|
|
220
|
-
registry.observer = observer
|
|
221
|
-
metric = registry.metric(:total_requests)
|
|
222
|
-
|
|
223
|
-
# Force cache to be invalid by invalidating it
|
|
224
|
-
metric.invalidate
|
|
225
|
-
|
|
226
|
-
# Set a value - should fall back to observer.set
|
|
227
|
-
metric.set(42)
|
|
228
|
-
expect(metric.value).to be == 42
|
|
229
|
-
|
|
230
|
-
# Verify it was written to shared memory
|
|
231
|
-
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
232
|
-
expect(buffer.get_value(:u64, 0)).to be == 42
|
|
233
|
-
end
|
|
234
|
-
|
|
235
219
|
it "handles write errors gracefully" do
|
|
236
220
|
registry.observer = observer
|
|
237
221
|
metric = registry.metric(:total_requests)
|
|
@@ -239,10 +223,6 @@ describe Async::Utilization::Metric do
|
|
|
239
223
|
# Set a value first to build the cache
|
|
240
224
|
metric.set(10)
|
|
241
225
|
|
|
242
|
-
# Verify cache is built
|
|
243
|
-
expect(metric.instance_variable_get(:@cache_valid)).to be == true
|
|
244
|
-
cached_buffer = metric.instance_variable_get(:@cached_buffer)
|
|
245
|
-
|
|
246
226
|
# Create an invalid buffer that will raise an error
|
|
247
227
|
invalid_buffer = Object.new
|
|
248
228
|
def invalid_buffer.set_value(type, offset, value)
|
|
@@ -251,13 +231,10 @@ describe Async::Utilization::Metric do
|
|
|
251
231
|
|
|
252
232
|
metric.instance_variable_set(:@cached_buffer, invalid_buffer)
|
|
253
233
|
|
|
254
|
-
# Should not raise, but log warning
|
|
234
|
+
# Should not raise, but log warning
|
|
255
235
|
metric.set(42)
|
|
256
236
|
expect(metric.value).to be == 42
|
|
257
237
|
|
|
258
|
-
# Cache should remain valid (not invalidated on error)
|
|
259
|
-
expect(metric.instance_variable_get(:@cache_valid)).to be == true
|
|
260
|
-
|
|
261
238
|
# Assert that a warning was logged
|
|
262
239
|
expect_console.to have_logged(
|
|
263
240
|
severity: be == :warn,
|
|
@@ -265,4 +242,27 @@ describe Async::Utilization::Metric do
|
|
|
265
242
|
message: be == "Failed to write metric value!"
|
|
266
243
|
)
|
|
267
244
|
end
|
|
245
|
+
|
|
246
|
+
it "clears cache when observer is removed" do
|
|
247
|
+
registry.observer = observer
|
|
248
|
+
metric = registry.metric(:total_requests)
|
|
249
|
+
metric.set(10)
|
|
250
|
+
|
|
251
|
+
# Remove observer — cache should be cleared
|
|
252
|
+
registry.observer = nil
|
|
253
|
+
|
|
254
|
+
# Write should not go to the old buffer
|
|
255
|
+
metric.set(99)
|
|
256
|
+
|
|
257
|
+
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
258
|
+
expect(buffer.get_value(:u64, 0)).to be == 10
|
|
259
|
+
|
|
260
|
+
# In-memory value is still updated
|
|
261
|
+
expect(metric.value).to be == 99
|
|
262
|
+
|
|
263
|
+
# Re-attaching observer should sync the current value
|
|
264
|
+
registry.observer = observer
|
|
265
|
+
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
266
|
+
expect(buffer.get_value(:u64, 0)).to be == 99
|
|
267
|
+
end
|
|
268
268
|
end
|
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require "sus"
|
|
7
|
-
require "sus/fixtures/console/captured_logger"
|
|
8
7
|
require "sus/fixtures/temporary_directory_context"
|
|
9
8
|
require "async/utilization"
|
|
10
9
|
require "fileutils"
|
|
11
10
|
|
|
12
11
|
describe Async::Utilization::Observer do
|
|
13
|
-
include Sus::Fixtures::Console::CapturedLogger
|
|
14
12
|
include Sus::Fixtures::TemporaryDirectoryContext
|
|
15
13
|
|
|
16
14
|
let(:shm_path) {File.join(root, "test.shm")}
|
|
@@ -41,68 +39,30 @@ describe Async::Utilization::Observer do
|
|
|
41
39
|
expect(observer.schema).to be == schema
|
|
42
40
|
end
|
|
43
41
|
|
|
44
|
-
it "can write values to shared memory" do
|
|
45
|
-
observer.set(:total_requests, 42)
|
|
46
|
-
observer.set(:active_requests, 5)
|
|
47
|
-
|
|
48
|
-
# Read back from file to verify
|
|
49
|
-
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
50
|
-
expect(buffer.get_value(:u64, 0)).to be == 42
|
|
51
|
-
expect(buffer.get_value(:u32, 8)).to be == 5
|
|
52
|
-
end
|
|
53
|
-
|
|
54
42
|
with "non-page-aligned offsets" do
|
|
55
43
|
let(:file_size) {IO::Buffer::PAGE_SIZE * 2}
|
|
56
44
|
let(:offset) {100} # Not page-aligned
|
|
57
45
|
|
|
58
|
-
it "
|
|
59
|
-
observer.
|
|
60
|
-
observer.
|
|
46
|
+
it "maps values at the correct offset" do
|
|
47
|
+
observer.buffer.set_value(:u64, schema[:total_requests].offset, 100)
|
|
48
|
+
observer.buffer.set_value(:u32, schema[:active_requests].offset, 20)
|
|
61
49
|
|
|
62
|
-
# Read back from file at the correct
|
|
50
|
+
# Read back from file at the correct byte position
|
|
63
51
|
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
64
|
-
expect(buffer.get_value(:u64, offset)).to be == 100
|
|
65
|
-
expect(buffer.get_value(:u32, offset +
|
|
52
|
+
expect(buffer.get_value(:u64, offset + schema[:total_requests].offset)).to be == 100
|
|
53
|
+
expect(buffer.get_value(:u32, offset + schema[:active_requests].offset)).to be == 20
|
|
66
54
|
end
|
|
67
55
|
end
|
|
68
56
|
|
|
69
|
-
it "ignores fields not in schema" do
|
|
70
|
-
# Should not raise an error
|
|
71
|
-
observer.set(:unknown_field, 999)
|
|
72
|
-
|
|
73
|
-
# Verify nothing was written
|
|
74
|
-
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
75
|
-
expect(buffer.get_value(:u64, 0)).to be == 0
|
|
76
|
-
end
|
|
77
|
-
|
|
78
57
|
with "page-aligned offsets" do
|
|
79
58
|
let(:file_size) {page_size * 2}
|
|
80
59
|
let(:segment_size) {page_size}
|
|
81
60
|
|
|
82
|
-
it "
|
|
83
|
-
|
|
84
|
-
observer.set(:total_requests, 123)
|
|
61
|
+
it "maps values at the correct offset" do
|
|
62
|
+
observer.buffer.set_value(:u64, schema[:total_requests].offset, 123)
|
|
85
63
|
|
|
86
|
-
# Verify value was written
|
|
87
64
|
buffer = IO::Buffer.map(File.open(shm_path, "r+b"), file_size, 0)
|
|
88
|
-
expect(buffer.get_value(:u64,
|
|
65
|
+
expect(buffer.get_value(:u64, schema[:total_requests].offset)).to be == 123
|
|
89
66
|
end
|
|
90
67
|
end
|
|
91
|
-
|
|
92
|
-
it "handles errors gracefully when setting values" do
|
|
93
|
-
# Create an invalid buffer that will cause an error
|
|
94
|
-
# We'll mock the buffer to raise an error
|
|
95
|
-
buffer = observer.instance_variable_get(:@buffer)
|
|
96
|
-
expect(buffer).to receive(:set_value).and_raise(IOError, "Buffer error")
|
|
97
|
-
|
|
98
|
-
# Should not raise, but log a warning
|
|
99
|
-
observer.set(:total_requests, 42)
|
|
100
|
-
|
|
101
|
-
# Assert that a warning was logged
|
|
102
|
-
expect_console.to have_logged(
|
|
103
|
-
severity: be == :warn,
|
|
104
|
-
subject: be_a(Async::Utilization::Observer),
|
|
105
|
-
message: be == "Failed to set field in shared memory!"
|
|
106
|
-
)
|
|
107
|
-
end
|
|
108
68
|
end
|
|
@@ -56,44 +56,24 @@ describe Async::Utilization::Registry do
|
|
|
56
56
|
expect(registry.values[:test_field]).to be == 0
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
it "can set an observer" do
|
|
60
|
-
observer = Object.new
|
|
61
|
-
def observer.set(field, value); end
|
|
62
|
-
|
|
63
|
-
registry.set(:test_field, 10)
|
|
64
|
-
registry.observer = observer
|
|
65
|
-
|
|
66
|
-
expect(registry.observer).to be == observer
|
|
67
|
-
end
|
|
68
|
-
|
|
69
59
|
it "notifies observer when values change" do
|
|
70
|
-
values_set = []
|
|
71
|
-
|
|
72
|
-
# Create a proper observer with schema
|
|
73
60
|
schema = Async::Utilization::Schema.build(test_field: :u64)
|
|
74
|
-
|
|
61
|
+
buffer = IO::Buffer.new(8)
|
|
75
62
|
|
|
76
|
-
|
|
77
|
-
observer.define_singleton_method(:set) do |field, value|
|
|
78
|
-
values_set << [field, value]
|
|
79
|
-
end
|
|
63
|
+
observer = Object.new
|
|
80
64
|
observer.define_singleton_method(:schema){schema}
|
|
81
|
-
observer.define_singleton_method(:buffer){
|
|
65
|
+
observer.define_singleton_method(:buffer){buffer}
|
|
82
66
|
|
|
83
67
|
registry.set(:test_field, 5)
|
|
84
68
|
registry.observer = observer
|
|
85
69
|
|
|
86
|
-
#
|
|
87
|
-
expect(
|
|
70
|
+
# Buffer should be synced with the existing value on observer assignment
|
|
71
|
+
expect(buffer.get_value(:u64, 0)).to be == 5
|
|
88
72
|
|
|
89
|
-
# Clear and test new changes
|
|
90
|
-
# Note: Since observer has no buffer, write_direct will return false
|
|
91
|
-
# and no notification will occur (as per new design)
|
|
92
|
-
values_set.clear
|
|
93
73
|
registry.increment(:test_field)
|
|
94
74
|
|
|
95
|
-
#
|
|
96
|
-
expect(
|
|
75
|
+
# Buffer should reflect the incremented value
|
|
76
|
+
expect(buffer.get_value(:u64, 0)).to be == 6
|
|
97
77
|
end
|
|
98
78
|
|
|
99
79
|
it "uses metric method for fast path" do
|
|
@@ -117,12 +97,35 @@ describe Async::Utilization::Registry do
|
|
|
117
97
|
expect(registry.values).to have_keys(module_set_test: be == 99)
|
|
118
98
|
end
|
|
119
99
|
|
|
120
|
-
it "
|
|
100
|
+
it "wires up existing metrics when observer is assigned" do
|
|
101
|
+
schema = Async::Utilization::Schema.build(test_field: :u64)
|
|
102
|
+
buffer = IO::Buffer.new(8)
|
|
103
|
+
observer = Object.new
|
|
104
|
+
observer.define_singleton_method(:schema){schema}
|
|
105
|
+
observer.define_singleton_method(:buffer){buffer}
|
|
106
|
+
|
|
107
|
+
registry.set(:test_field, 7)
|
|
108
|
+
registry.observer = observer
|
|
109
|
+
|
|
110
|
+
expect(buffer.get_value(:u64, 0)).to be == 7
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "stops writing to buffer when observer is removed" do
|
|
114
|
+
schema = Async::Utilization::Schema.build(test_field: :u64)
|
|
115
|
+
buffer = IO::Buffer.new(8)
|
|
121
116
|
observer = Object.new
|
|
122
|
-
|
|
117
|
+
observer.define_singleton_method(:schema){schema}
|
|
118
|
+
observer.define_singleton_method(:buffer){buffer}
|
|
123
119
|
|
|
124
120
|
registry.observer = observer
|
|
121
|
+
registry.set(:test_field, 5)
|
|
122
|
+
expect(buffer.get_value(:u64, 0)).to be == 5
|
|
123
|
+
|
|
124
|
+
registry.observer = nil
|
|
125
|
+
registry.set(:test_field, 99)
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
# Buffer unchanged, in-memory value updated
|
|
128
|
+
expect(buffer.get_value(:u64, 0)).to be == 5
|
|
129
|
+
expect(registry.values[:test_field]).to be == 99
|
|
127
130
|
end
|
|
128
131
|
end
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|