async-utilization 0.4.0 → 0.5.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/segment_store.rb +208 -0
- data/lib/async/utilization/version.rb +1 -1
- data/lib/async/utilization.rb +1 -0
- data/readme.md +11 -7
- data/releases.md +4 -0
- data/test/async/utilization/segment_store.rb +175 -0
- data.tar.gz.sig +0 -0
- metadata +6 -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: 64e873daf011f6226b574b609cecde9b402edb2a2ff5144f68526195575492ea
|
|
4
|
+
data.tar.gz: b5e25e805f9e8b359af97653bc1496706c9ac81d8c8c2ea42c1bbd1680be271b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e486c1307fead9186ae112f2c258ec4e5ed25b7c6cb8d49d56974634ed2e250400ccb8a57caccb736871df8cd592e5be5379bfd9ac738cf9ace363812e651a90
|
|
7
|
+
data.tar.gz: 4c0380449a03ea7b1ba5c42cc0b70e86cbe76fb360e0bdccaf7f3b5477dccd26d0171921e91f2e535433a236c60c8abb8a97f3dcaf1c6e34b17d7e8c87a72a17
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "console"
|
|
7
|
+
|
|
8
|
+
module Async
|
|
9
|
+
module Utilization
|
|
10
|
+
# Represents a shared memory segment store for utilization data.
|
|
11
|
+
#
|
|
12
|
+
# Stores fixed-size segments in a shared memory file, associates each
|
|
13
|
+
# segment with a utilization schema, and reads the resulting values.
|
|
14
|
+
class SegmentStore
|
|
15
|
+
# Open a shared memory segment store.
|
|
16
|
+
#
|
|
17
|
+
# @parameter path [String] The path to the shared memory file.
|
|
18
|
+
# @parameter size [Integer] The initial size of the shared memory file.
|
|
19
|
+
# @parameter segment_size [Integer] The size of each allocation segment.
|
|
20
|
+
# @parameter growth_factor [Integer | Float] The factor used to grow the file when all segments are allocated.
|
|
21
|
+
# @parameter replace [Boolean] Whether to replace an existing file at the given path.
|
|
22
|
+
# @yields {|store| ...} The store, which is closed after the block completes.
|
|
23
|
+
# @parameter store [SegmentStore] The opened store.
|
|
24
|
+
# @returns [SegmentStore | Object] The store, or the value returned by the block.
|
|
25
|
+
# @raises [ArgumentError] If the store configuration is invalid.
|
|
26
|
+
# @raises [Errno::EEXIST] If the path already exists and `replace` is `false`.
|
|
27
|
+
def self.open(path, size: IO::Buffer::PAGE_SIZE * 8, segment_size: 512, growth_factor: 2, replace: false)
|
|
28
|
+
raise ArgumentError, "Size must be a positive integer!" unless size.is_a?(Integer) && size > 0
|
|
29
|
+
raise ArgumentError, "Segment size must be a positive integer!" unless segment_size.is_a?(Integer) && segment_size > 0
|
|
30
|
+
raise ArgumentError, "Segment size must not exceed size!" if segment_size > size
|
|
31
|
+
raise ArgumentError, "Growth factor must be greater than 1!" unless growth_factor.is_a?(Numeric) && growth_factor.real? && growth_factor > 1
|
|
32
|
+
|
|
33
|
+
if replace
|
|
34
|
+
begin
|
|
35
|
+
File.unlink(path)
|
|
36
|
+
rescue Errno::ENOENT
|
|
37
|
+
# The file does not need to be replaced:
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
file = File.open(path, "w+bx")
|
|
42
|
+
buffer = nil
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
file.truncate(size)
|
|
46
|
+
buffer = IO::Buffer.map(file, size)
|
|
47
|
+
store = new(file, buffer, size: size, segment_size: segment_size, growth_factor: growth_factor)
|
|
48
|
+
rescue
|
|
49
|
+
buffer&.free
|
|
50
|
+
file.close
|
|
51
|
+
raise
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
return store unless block_given?
|
|
55
|
+
|
|
56
|
+
begin
|
|
57
|
+
yield store
|
|
58
|
+
ensure
|
|
59
|
+
store.close
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Initialize the shared memory segment store.
|
|
64
|
+
#
|
|
65
|
+
# @parameter file [File] The open shared memory file.
|
|
66
|
+
# @parameter buffer [IO::Buffer] The mapped shared memory buffer.
|
|
67
|
+
# @parameter size [Integer] The initial size of the shared memory file.
|
|
68
|
+
# @parameter segment_size [Integer] The size of each allocation segment.
|
|
69
|
+
# @parameter growth_factor [Integer | Float] The factor used to grow the file when all segments are allocated.
|
|
70
|
+
def initialize(file, buffer, size:, segment_size:, growth_factor:)
|
|
71
|
+
@file = file
|
|
72
|
+
@buffer = buffer
|
|
73
|
+
@size = size
|
|
74
|
+
@segment_size = segment_size
|
|
75
|
+
@growth_factor = growth_factor
|
|
76
|
+
|
|
77
|
+
@allocations = {}
|
|
78
|
+
@free_list = []
|
|
79
|
+
|
|
80
|
+
(0...(@size / @segment_size)).each do |segment_index|
|
|
81
|
+
@free_list << (segment_index * @segment_size)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Allocate a segment for the given key.
|
|
86
|
+
#
|
|
87
|
+
# The shared memory file is automatically resized if no segments are available.
|
|
88
|
+
#
|
|
89
|
+
# @parameter key [Object] The key used to identify the allocation.
|
|
90
|
+
# @parameter schema [Array] The `[key, type, offset]` tuples describing the data layout.
|
|
91
|
+
# @returns [Integer | Nil] The offset into the shared memory file, or `nil` if allocation fails.
|
|
92
|
+
def allocate(key, schema)
|
|
93
|
+
if @free_list.empty?
|
|
94
|
+
unless resize(@size * @growth_factor)
|
|
95
|
+
return nil
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
offset = @free_list.shift
|
|
100
|
+
@allocations[key] = {offset: offset, schema: schema}
|
|
101
|
+
|
|
102
|
+
return offset
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Free the segment allocated to the given key.
|
|
106
|
+
#
|
|
107
|
+
# @parameter key [Object] The key used to identify the allocation.
|
|
108
|
+
def free(key)
|
|
109
|
+
if allocation = @allocations.delete(key)
|
|
110
|
+
@free_list << allocation[:offset]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Get the allocation information for the given key.
|
|
115
|
+
#
|
|
116
|
+
# @parameter key [Object] The key used to identify the allocation.
|
|
117
|
+
# @returns [Hash | Nil] The allocation offset and schema, or `nil` if the key is not allocated.
|
|
118
|
+
def allocation(key)
|
|
119
|
+
@allocations[key]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @attribute [Integer] The current size of the shared memory file.
|
|
123
|
+
attr :size
|
|
124
|
+
|
|
125
|
+
# Update the schema for an existing allocation.
|
|
126
|
+
#
|
|
127
|
+
# @parameter key [Object] The key used to identify the allocation.
|
|
128
|
+
# @parameter schema [Array] The `[key, type, offset]` tuples describing the data layout.
|
|
129
|
+
def update_schema(key, schema)
|
|
130
|
+
if allocation = @allocations[key]
|
|
131
|
+
allocation[:schema] = schema
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Read utilization data from an allocated segment.
|
|
136
|
+
#
|
|
137
|
+
# @parameter key [Object] The key used to identify the allocation.
|
|
138
|
+
# @returns [Hash | Nil] The utilization values, or `nil` if the key is not allocated.
|
|
139
|
+
def read(key)
|
|
140
|
+
allocation = @allocations[key]
|
|
141
|
+
return nil unless allocation
|
|
142
|
+
|
|
143
|
+
offset = allocation[:offset]
|
|
144
|
+
schema = allocation[:schema]
|
|
145
|
+
|
|
146
|
+
result = {}
|
|
147
|
+
schema.each do |field_key, type, field_offset|
|
|
148
|
+
absolute_offset = offset + field_offset
|
|
149
|
+
|
|
150
|
+
begin
|
|
151
|
+
result[field_key] = @buffer.get_value(type, absolute_offset)
|
|
152
|
+
rescue => error
|
|
153
|
+
Console.warn(self, "Failed to read value", type: type, key: field_key, offset: absolute_offset, exception: error)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
return result
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Resize the shared memory file.
|
|
161
|
+
#
|
|
162
|
+
# The new size is rounded up to the nearest page boundary.
|
|
163
|
+
#
|
|
164
|
+
# @parameter new_size [Integer] The requested new size of the shared memory file.
|
|
165
|
+
# @returns [Boolean] Whether the file was resized successfully.
|
|
166
|
+
def resize(new_size)
|
|
167
|
+
old_size = @size
|
|
168
|
+
return false if new_size <= old_size
|
|
169
|
+
|
|
170
|
+
page_size = IO::Buffer::PAGE_SIZE
|
|
171
|
+
new_size = (((new_size + page_size - 1) / page_size) * page_size).to_i
|
|
172
|
+
|
|
173
|
+
begin
|
|
174
|
+
@file.truncate(new_size)
|
|
175
|
+
buffer = IO::Buffer.map(@file, new_size)
|
|
176
|
+
|
|
177
|
+
@buffer&.free
|
|
178
|
+
@buffer = buffer
|
|
179
|
+
|
|
180
|
+
old_segment_count = old_size / @segment_size
|
|
181
|
+
new_segment_count = new_size / @segment_size
|
|
182
|
+
|
|
183
|
+
(old_segment_count...new_segment_count).each do |segment_index|
|
|
184
|
+
@free_list << (segment_index * @segment_size)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
@size = new_size
|
|
188
|
+
|
|
189
|
+
Console.info(self, "Resized shared memory", old_size: old_size, new_size: new_size, segments_added: new_segment_count - old_segment_count)
|
|
190
|
+
|
|
191
|
+
return true
|
|
192
|
+
rescue => error
|
|
193
|
+
Console.error(self, "Failed to resize shared memory", old_size: old_size, new_size: new_size, exception: error)
|
|
194
|
+
return false
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Close the shared memory file.
|
|
199
|
+
def close
|
|
200
|
+
@buffer&.free
|
|
201
|
+
@buffer = nil
|
|
202
|
+
|
|
203
|
+
@file&.close
|
|
204
|
+
@file = nil
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
data/lib/async/utilization.rb
CHANGED
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.5.0
|
|
18
|
+
|
|
19
|
+
- Add `Async::Utilization::SegmentStore` for allocating and reading utilization data in shared memory.
|
|
20
|
+
|
|
17
21
|
### v0.4.0
|
|
18
22
|
|
|
19
23
|
- Add `Async::Utilization::Namespace` for composing registry metric names.
|
|
@@ -39,26 +43,26 @@ Please see the [project releases](https://socketry.github.io/async-utilization/r
|
|
|
39
43
|
|
|
40
44
|
We welcome contributions to this project.
|
|
41
45
|
|
|
42
|
-
1. Fork
|
|
46
|
+
1. Fork the repository.
|
|
43
47
|
2. Create your feature branch (`git checkout -b my-new-feature`).
|
|
44
|
-
3. Commit your changes (`git commit -am 'Add some feature'`).
|
|
48
|
+
3. Commit your changes (`git commit -am 'Add some feature.'`).
|
|
45
49
|
4. Push to the branch (`git push origin my-new-feature`).
|
|
46
|
-
5. Create new
|
|
50
|
+
5. Create a new pull request.
|
|
47
51
|
|
|
48
52
|
### Running Tests
|
|
49
53
|
|
|
50
54
|
To run the test suite:
|
|
51
55
|
|
|
52
|
-
```
|
|
53
|
-
bundle exec sus
|
|
56
|
+
``` bash
|
|
57
|
+
$ bundle exec sus
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
### Making Releases
|
|
57
61
|
|
|
58
62
|
To make a new release:
|
|
59
63
|
|
|
60
|
-
```
|
|
61
|
-
bundle exec bake gem:release:patch # or minor or major
|
|
64
|
+
``` bash
|
|
65
|
+
$ bundle exec bake gem:release:patch # or minor or major
|
|
62
66
|
```
|
|
63
67
|
|
|
64
68
|
### Developer Certificate of Origin
|
data/releases.md
CHANGED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "sus"
|
|
7
|
+
require "sus/fixtures/console/null_logger"
|
|
8
|
+
require "sus/fixtures/temporary_directory_context"
|
|
9
|
+
require "async/utilization"
|
|
10
|
+
|
|
11
|
+
describe Async::Utilization::SegmentStore do
|
|
12
|
+
include Sus::Fixtures::Console::NullLogger
|
|
13
|
+
include Sus::Fixtures::TemporaryDirectoryContext
|
|
14
|
+
|
|
15
|
+
let(:path) {File.join(root, "utilization.shm")}
|
|
16
|
+
let(:page_size) {IO::Buffer::PAGE_SIZE}
|
|
17
|
+
let(:schema) do
|
|
18
|
+
Async::Utilization::Schema.build(
|
|
19
|
+
requests_total: :u64,
|
|
20
|
+
requests_active: :u32,
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "allocates, reads, and reuses segments" do
|
|
25
|
+
store = subject.open(path, size: page_size, segment_size: page_size)
|
|
26
|
+
|
|
27
|
+
first_offset = store.allocate(:first, [])
|
|
28
|
+
expect(first_offset).to be == 0
|
|
29
|
+
expect(store.allocation(:first)).to have_keys(offset: be == 0, schema: be == [])
|
|
30
|
+
|
|
31
|
+
store.update_schema(:first, schema.to_a)
|
|
32
|
+
observer = Async::Utilization::Observer.open(schema, path, page_size, first_offset)
|
|
33
|
+
observer.buffer.set_value(:u64, 0, 12)
|
|
34
|
+
observer.buffer.set_value(:u32, 8, 3)
|
|
35
|
+
|
|
36
|
+
expect(store.read(:first)).to be == {requests_total: 12, requests_active: 3}
|
|
37
|
+
|
|
38
|
+
store.free(:first)
|
|
39
|
+
expect(store.read(:first)).to be_nil
|
|
40
|
+
expect(store.allocate(:second, schema.to_a)).to be == first_offset
|
|
41
|
+
ensure
|
|
42
|
+
observer&.buffer&.free
|
|
43
|
+
store&.close
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "preserves existing observer mappings when resizing" do
|
|
47
|
+
store = subject.open(path, size: page_size, segment_size: page_size)
|
|
48
|
+
first_offset = store.allocate(:first, schema.to_a)
|
|
49
|
+
observer = Async::Utilization::Observer.open(schema, path, page_size, first_offset)
|
|
50
|
+
|
|
51
|
+
observer.buffer.set_value(:u64, 0, 42)
|
|
52
|
+
expect(store.read(:first)[:requests_total]).to be == 42
|
|
53
|
+
|
|
54
|
+
second_offset = store.allocate(:second, schema.to_a)
|
|
55
|
+
expect(second_offset).to be == page_size
|
|
56
|
+
expect(store.size).to be == page_size * 2
|
|
57
|
+
|
|
58
|
+
observer.buffer.set_value(:u64, 0, 99)
|
|
59
|
+
expect(store.read(:first)[:requests_total]).to be == 99
|
|
60
|
+
ensure
|
|
61
|
+
observer&.buffer&.free
|
|
62
|
+
store&.close
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "returns nil when automatic resizing fails" do
|
|
66
|
+
store = subject.open(path, size: page_size, segment_size: page_size)
|
|
67
|
+
store.allocate(:first, schema.to_a)
|
|
68
|
+
|
|
69
|
+
expect(store).to receive(:resize).and_return(false)
|
|
70
|
+
expect(store.allocate(:second, schema.to_a)).to be_nil
|
|
71
|
+
ensure
|
|
72
|
+
store&.close
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "skips fields that cannot be read" do
|
|
76
|
+
store = subject.open(path, size: page_size, segment_size: page_size)
|
|
77
|
+
store.allocate(:worker, [[:invalid, :invalid, 0]])
|
|
78
|
+
|
|
79
|
+
expect(store.read(:worker)).to be == {}
|
|
80
|
+
ensure
|
|
81
|
+
store&.close
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "reports resize failures" do
|
|
85
|
+
store = subject.open(path, size: page_size, segment_size: page_size)
|
|
86
|
+
file = store.instance_variable_get(:@file)
|
|
87
|
+
|
|
88
|
+
expect(file).to receive(:truncate).and_raise(IOError, "Failed to resize")
|
|
89
|
+
expect(store.resize(page_size * 2)).to be_falsey
|
|
90
|
+
ensure
|
|
91
|
+
store&.close
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "only replaces an existing file when requested" do
|
|
95
|
+
original = subject.open(path, size: page_size, segment_size: page_size)
|
|
96
|
+
original.resize(page_size * 2)
|
|
97
|
+
|
|
98
|
+
existing_file = File.open(path, "rb")
|
|
99
|
+
original_size = existing_file.size
|
|
100
|
+
original.close
|
|
101
|
+
|
|
102
|
+
expect do
|
|
103
|
+
subject.open(path, size: page_size, segment_size: page_size)
|
|
104
|
+
end.to raise_exception(Errno::EEXIST)
|
|
105
|
+
|
|
106
|
+
replacement = subject.open(path, size: page_size, segment_size: page_size, replace: true)
|
|
107
|
+
expect(replacement.size).to be == page_size
|
|
108
|
+
expect(existing_file.size).to be == original_size
|
|
109
|
+
ensure
|
|
110
|
+
original&.close
|
|
111
|
+
replacement&.close
|
|
112
|
+
existing_file&.close
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "validates configuration before replacing an existing file" do
|
|
116
|
+
File.write(path, "existing")
|
|
117
|
+
|
|
118
|
+
[
|
|
119
|
+
[{size: 0}, "Size must be a positive integer!"],
|
|
120
|
+
[{segment_size: 0}, "Segment size must be a positive integer!"],
|
|
121
|
+
[{size: page_size, segment_size: page_size * 2}, "Segment size must not exceed size!"],
|
|
122
|
+
[{growth_factor: 1}, "Growth factor must be greater than 1!"],
|
|
123
|
+
].each do |options, message|
|
|
124
|
+
expect do
|
|
125
|
+
subject.open(path, replace: true, **options)
|
|
126
|
+
end.to raise_exception(ArgumentError, message: be == message)
|
|
127
|
+
|
|
128
|
+
expect(File.read(path)).to be == "existing"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "closes the store after yielding it" do
|
|
133
|
+
file = nil
|
|
134
|
+
|
|
135
|
+
result = subject.open(path, size: page_size, segment_size: page_size) do |store|
|
|
136
|
+
file = store.instance_variable_get(:@file)
|
|
137
|
+
expect(file.closed?).to be_falsey
|
|
138
|
+
:result
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
expect(result).to be == :result
|
|
142
|
+
expect(file.closed?).to be_truthy
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "closes the file when mapping fails" do
|
|
146
|
+
file = File.open(path, "w+bx")
|
|
147
|
+
File.unlink(path)
|
|
148
|
+
|
|
149
|
+
expect(File).to receive(:open).with(path, "w+bx").and_return(file)
|
|
150
|
+
expect(IO::Buffer).to receive(:map).with(file, page_size).and_raise(IOError, "Failed to map")
|
|
151
|
+
|
|
152
|
+
expect do
|
|
153
|
+
subject.open(path, size: page_size, segment_size: page_size)
|
|
154
|
+
end.to raise_exception(IOError, message: be == "Failed to map")
|
|
155
|
+
|
|
156
|
+
expect(file.closed?).to be_truthy
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "releases acquired resources when initialization fails" do
|
|
160
|
+
file = File.open(path, "w+bx")
|
|
161
|
+
File.unlink(path)
|
|
162
|
+
buffer = IO::Buffer.new(page_size)
|
|
163
|
+
|
|
164
|
+
expect(File).to receive(:open).with(path, "w+bx").and_return(file)
|
|
165
|
+
expect(IO::Buffer).to receive(:map).with(file, page_size).and_return(buffer)
|
|
166
|
+
expect(subject).to receive(:new).and_raise(IOError, "Failed to initialize")
|
|
167
|
+
|
|
168
|
+
expect do
|
|
169
|
+
subject.open(path, size: page_size, segment_size: page_size)
|
|
170
|
+
end.to raise_exception(IOError, message: be == "Failed to initialize")
|
|
171
|
+
|
|
172
|
+
expect(file.closed?).to be_truthy
|
|
173
|
+
expect(buffer.null?).to be_truthy
|
|
174
|
+
end
|
|
175
|
+
end
|
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.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- lib/async/utilization/observer.rb
|
|
63
63
|
- lib/async/utilization/registry.rb
|
|
64
64
|
- lib/async/utilization/schema.rb
|
|
65
|
+
- lib/async/utilization/segment_store.rb
|
|
65
66
|
- lib/async/utilization/version.rb
|
|
66
67
|
- license.md
|
|
67
68
|
- readme.md
|
|
@@ -72,10 +73,13 @@ files:
|
|
|
72
73
|
- test/async/utilization/observer.rb
|
|
73
74
|
- test/async/utilization/registry.rb
|
|
74
75
|
- test/async/utilization/schema.rb
|
|
76
|
+
- test/async/utilization/segment_store.rb
|
|
75
77
|
homepage: https://github.com/socketry/async-utilization
|
|
76
78
|
licenses:
|
|
77
79
|
- MIT
|
|
78
80
|
metadata:
|
|
81
|
+
bug_tracker_uri: https://github.com/socketry/async-utilization/issues
|
|
82
|
+
changelog_uri: https://github.com/socketry/async-utilization/blob/main/releases.md
|
|
79
83
|
documentation_uri: https://socketry.github.io/async-utilization/
|
|
80
84
|
source_code_uri: https://github.com/socketry/async-utilization.git
|
|
81
85
|
rdoc_options: []
|
|
@@ -92,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
92
96
|
- !ruby/object:Gem::Version
|
|
93
97
|
version: '0'
|
|
94
98
|
requirements: []
|
|
95
|
-
rubygems_version: 4.0.
|
|
99
|
+
rubygems_version: 4.0.10
|
|
96
100
|
specification_version: 4
|
|
97
101
|
summary: High-performance utilization metrics for Async services using shared memory.
|
|
98
102
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|