prometheus-client-mmap 0.16.2 → 0.18.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
- data/README.md +22 -10
- data/ext/fast_mmaped_file/hashmap.c +467 -410
- data/ext/fast_mmaped_file/jsmn.c +244 -259
- data/ext/fast_mmaped_file/mmap.c +58 -0
- data/ext/fast_mmaped_file/mmap.h +6 -0
- data/ext/fast_mmaped_file/value_access.c +9 -6
- data/lib/fast_mmaped_file.bundle +0 -0
- data/lib/mmap.rb +7 -0
- data/lib/prometheus/#client.rb# +58 -0
- data/lib/prometheus/client/push.rb +120 -12
- data/lib/prometheus/client/version.rb +1 -1
- metadata +6 -4
@@ -66,7 +66,7 @@ static int perform_mmap(mm_ipc *i_mm, size_t len) {
|
|
66
66
|
return SUCCESS;
|
67
67
|
}
|
68
68
|
|
69
|
-
static int expand(mm_ipc *i_mm, size_t len) {
|
69
|
+
static int expand(VALUE self, mm_ipc *i_mm, size_t len) {
|
70
70
|
if (len < i_mm->t->len) {
|
71
71
|
return with_exception(rb_eArgError, "Can't reduce the size of mmap");
|
72
72
|
}
|
@@ -88,6 +88,8 @@ static int expand(mm_ipc *i_mm, size_t len) {
|
|
88
88
|
return with_exception_errno(rb_eArgError, "mlock(%d)", errno);
|
89
89
|
}
|
90
90
|
|
91
|
+
mm_update(self);
|
92
|
+
|
91
93
|
return SUCCESS;
|
92
94
|
}
|
93
95
|
|
@@ -151,7 +153,7 @@ uint32_t load_used(mm_ipc *i_mm) {
|
|
151
153
|
|
152
154
|
void save_used(mm_ipc *i_mm, uint32_t used) { *((uint32_t *)i_mm->t->addr) = used; }
|
153
155
|
|
154
|
-
static VALUE initialize_entry(mm_ipc *i_mm, VALUE positions, VALUE key, VALUE value) {
|
156
|
+
static VALUE initialize_entry(VALUE self, mm_ipc *i_mm, VALUE positions, VALUE key, VALUE value) {
|
155
157
|
if (i_mm->t->flag & MM_FROZEN) {
|
156
158
|
rb_error_frozen("mmap");
|
157
159
|
}
|
@@ -166,10 +168,11 @@ static VALUE initialize_entry(mm_ipc *i_mm, VALUE positions, VALUE key, VALUE va
|
|
166
168
|
|
167
169
|
uint32_t used = load_used(i_mm);
|
168
170
|
while (i_mm->t->len < (used + entry_length)) {
|
169
|
-
if (!expand(i_mm, i_mm->t->len * 2)) {
|
171
|
+
if (!expand(self, i_mm, i_mm->t->len * 2)) {
|
170
172
|
raise_last_exception();
|
171
173
|
}
|
172
174
|
}
|
175
|
+
|
173
176
|
save_entry(i_mm, used, key, value);
|
174
177
|
save_used(i_mm, used + entry_length);
|
175
178
|
|
@@ -189,7 +192,7 @@ VALUE method_fetch_entry(VALUE self, VALUE positions, VALUE key, VALUE default_v
|
|
189
192
|
return load_value(i_mm, position);
|
190
193
|
}
|
191
194
|
|
192
|
-
position = initialize_entry(i_mm, positions, key, default_value);
|
195
|
+
position = initialize_entry(self, i_mm, positions, key, default_value);
|
193
196
|
return load_value(i_mm, position);
|
194
197
|
}
|
195
198
|
|
@@ -207,7 +210,7 @@ VALUE method_upsert_entry(VALUE self, VALUE positions, VALUE key, VALUE value) {
|
|
207
210
|
return load_value(i_mm, position);
|
208
211
|
}
|
209
212
|
|
210
|
-
position = initialize_entry(i_mm, positions, key, value);
|
213
|
+
position = initialize_entry(self, i_mm, positions, key, value);
|
211
214
|
return load_value(i_mm, position);
|
212
215
|
}
|
213
216
|
|
@@ -229,7 +232,7 @@ VALUE method_save_used(VALUE self, VALUE value) {
|
|
229
232
|
}
|
230
233
|
|
231
234
|
if (i_mm->t->len < INITIAL_SIZE) {
|
232
|
-
if (!expand(i_mm, INITIAL_SIZE)) {
|
235
|
+
if (!expand(self, i_mm, INITIAL_SIZE)) {
|
233
236
|
raise_last_exception();
|
234
237
|
}
|
235
238
|
}
|
data/lib/fast_mmaped_file.bundle
CHANGED
Binary file
|
data/lib/mmap.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'prometheus/client/registry'
|
2
|
+
require 'prometheus/client/configuration'
|
3
|
+
require 'prometheus/client/mmaped_value'
|
4
|
+
|
5
|
+
module Prometheus
|
6
|
+
# Client is a ruby implementation for a Prometheus compatible client.
|
7
|
+
module Client
|
8
|
+
class << self
|
9
|
+
attr_writer :configuration
|
10
|
+
|
11
|
+
def configuration
|
12
|
+
@configuration ||= Configuration.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def configure
|
16
|
+
yield(configuration)
|
17
|
+
end
|
18
|
+
o
|
19
|
+
# Returns a default registry object
|
20
|
+
def registry
|
21
|
+
@registry ||= Registry.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
configuration.logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def pid
|
29
|
+
configuration.pid_provider.call
|
30
|
+
end
|
31
|
+
|
32
|
+
# Resets the registry and reinitializes all metrics files.
|
33
|
+
# Use case: clean up everything in specs `before` block,
|
34
|
+
# to prevent leaking the state between specs which are updating metrics.
|
35
|
+
def reset!
|
36
|
+
@registry = nil
|
37
|
+
::Prometheus::Client::MmapedValue.reset_and_reinitialize
|
38
|
+
end
|
39
|
+
|
40
|
+
def cleanup!
|
41
|
+
Dir.glob("#{configuration.multiprocess_files_dir}/*.db").each { |f| File.unlink(f) if File.exist?(f) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# With `force: false`: reinitializes metric files only for processes with the changed PID.
|
45
|
+
# With `force: true`: reinitializes all metrics files.
|
46
|
+
# Always keeps the registry.
|
47
|
+
# Use case (`force: false`): pick up new metric files on each worker start,
|
48
|
+
# without resetting already registered files for the master or previously initialized workers.
|
49
|
+
def reinitialize_on_pid_change(force: false)
|
50
|
+
if force
|
51
|
+
::Prometheus::Client::MmapedValue.reset_and_reinitialize
|
52
|
+
else
|
53
|
+
::Prometheus::Client::MmapedValue.reinitialize_on_pid_change
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
+
require 'base64'
|
3
4
|
require 'thread'
|
4
5
|
require 'net/http'
|
5
6
|
require 'uri'
|
6
7
|
require 'erb'
|
8
|
+
require 'set'
|
7
9
|
|
8
10
|
require 'prometheus/client'
|
9
11
|
require 'prometheus/client/formats/text'
|
12
|
+
require 'prometheus/client/label_set_validator'
|
10
13
|
|
11
14
|
module Prometheus
|
12
15
|
# Client is a ruby implementation for a Prometheus compatible client.
|
@@ -14,23 +17,31 @@ module Prometheus
|
|
14
17
|
# Push implements a simple way to transmit a given registry to a given
|
15
18
|
# Pushgateway.
|
16
19
|
class Push
|
20
|
+
class HttpError < StandardError; end
|
21
|
+
class HttpRedirectError < HttpError; end
|
22
|
+
class HttpClientError < HttpError; end
|
23
|
+
class HttpServerError < HttpError; end
|
24
|
+
|
17
25
|
DEFAULT_GATEWAY = 'http://localhost:9091'.freeze
|
18
26
|
PATH = '/metrics/job/%s'.freeze
|
19
|
-
INSTANCE_PATH = '/metrics/job/%s/instance/%s'.freeze
|
20
27
|
SUPPORTED_SCHEMES = %w(http https).freeze
|
21
28
|
|
22
|
-
attr_reader :job, :
|
29
|
+
attr_reader :job, :gateway, :path
|
23
30
|
|
24
|
-
def initialize(job:,
|
31
|
+
def initialize(job:, gateway: DEFAULT_GATEWAY, grouping_key: {}, **kwargs)
|
25
32
|
raise ArgumentError, "job cannot be nil" if job.nil?
|
26
33
|
raise ArgumentError, "job cannot be empty" if job.empty?
|
34
|
+
@validator = LabelSetValidator.new()
|
35
|
+
@validator.validate(grouping_key)
|
27
36
|
|
28
37
|
@mutex = Mutex.new
|
29
38
|
@job = job
|
30
|
-
@instance = instance
|
31
39
|
@gateway = gateway || DEFAULT_GATEWAY
|
32
|
-
@
|
40
|
+
@grouping_key = grouping_key
|
41
|
+
@path = build_path(job, grouping_key)
|
42
|
+
|
33
43
|
@uri = parse("#{@gateway}#{@path}")
|
44
|
+
validate_no_basic_auth!(@uri)
|
34
45
|
|
35
46
|
@http = Net::HTTP.new(@uri.host, @uri.port)
|
36
47
|
@http.use_ssl = (@uri.scheme == 'https')
|
@@ -38,6 +49,11 @@ module Prometheus
|
|
38
49
|
@http.read_timeout = kwargs[:read_timeout] if kwargs[:read_timeout]
|
39
50
|
end
|
40
51
|
|
52
|
+
def basic_auth(user, password)
|
53
|
+
@user = user
|
54
|
+
@password = password
|
55
|
+
end
|
56
|
+
|
41
57
|
def add(registry)
|
42
58
|
synchronize do
|
43
59
|
request(Net::HTTP::Post, registry)
|
@@ -70,26 +86,118 @@ module Prometheus
|
|
70
86
|
raise ArgumentError, "#{url} is not a valid URL: #{e}"
|
71
87
|
end
|
72
88
|
|
73
|
-
def build_path(job,
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
89
|
+
def build_path(job, grouping_key)
|
90
|
+
path = format(PATH, ERB::Util::url_encode(job))
|
91
|
+
|
92
|
+
grouping_key.each do |label, value|
|
93
|
+
if value.include?('/')
|
94
|
+
encoded_value = Base64.urlsafe_encode64(value)
|
95
|
+
path += "/#{label}@base64/#{encoded_value}"
|
96
|
+
# While it's valid for the urlsafe_encode64 function to return an
|
97
|
+
# empty string when the input string is empty, it doesn't work for
|
98
|
+
# our specific use case as we're putting the result into a URL path
|
99
|
+
# segment. A double slash (`//`) can be normalised away by HTTP
|
100
|
+
# libraries, proxies, and web servers.
|
101
|
+
#
|
102
|
+
# For empty strings, we use a single padding character (`=`) as the
|
103
|
+
# value.
|
104
|
+
#
|
105
|
+
# See the pushgateway docs for more details:
|
106
|
+
#
|
107
|
+
# https://github.com/prometheus/pushgateway/blob/6393a901f56d4dda62cd0f6ab1f1f07c495b6354/README.md#url
|
108
|
+
elsif value.empty?
|
109
|
+
path += "/#{label}@base64/="
|
110
|
+
else
|
111
|
+
path += "/#{label}/#{ERB::Util::url_encode(value)}"
|
112
|
+
end
|
78
113
|
end
|
114
|
+
|
115
|
+
path
|
79
116
|
end
|
80
117
|
|
81
118
|
def request(req_class, registry = nil)
|
119
|
+
validate_no_label_clashes!(registry) if registry
|
120
|
+
|
82
121
|
req = req_class.new(@uri)
|
83
122
|
req.content_type = Formats::Text::CONTENT_TYPE
|
84
|
-
req.basic_auth(@
|
123
|
+
req.basic_auth(@user, @password) if @user
|
85
124
|
req.body = Formats::Text.marshal(registry) if registry
|
86
125
|
|
87
|
-
@http.request(req)
|
126
|
+
response = @http.request(req)
|
127
|
+
validate_response!(response)
|
128
|
+
|
129
|
+
response
|
88
130
|
end
|
89
131
|
|
90
132
|
def synchronize
|
91
133
|
@mutex.synchronize { yield }
|
92
134
|
end
|
135
|
+
|
136
|
+
def validate_no_basic_auth!(uri)
|
137
|
+
if uri.user || uri.password
|
138
|
+
raise ArgumentError, <<~EOF
|
139
|
+
Setting Basic Auth credentials in the gateway URL is not supported, please call the `basic_auth` method.
|
140
|
+
|
141
|
+
Received username `#{uri.user}` in gateway URL. Instead of passing
|
142
|
+
Basic Auth credentials like this:
|
143
|
+
|
144
|
+
```
|
145
|
+
push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://user:password@localhost:9091")
|
146
|
+
```
|
147
|
+
|
148
|
+
please pass them like this:
|
149
|
+
|
150
|
+
```
|
151
|
+
push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://localhost:9091")
|
152
|
+
push.basic_auth("user", "password")
|
153
|
+
```
|
154
|
+
|
155
|
+
While URLs do support passing Basic Auth credentials using the
|
156
|
+
`http://user:password@example.com/` syntax, the username and
|
157
|
+
password in that syntax have to follow the usual rules for URL
|
158
|
+
encoding of characters per RFC 3986
|
159
|
+
(https://datatracker.ietf.org/doc/html/rfc3986#section-2.1).
|
160
|
+
|
161
|
+
Rather than place the burden of correctly performing that encoding
|
162
|
+
on users of this gem, we decided to have a separate method for
|
163
|
+
supplying Basic Auth credentials, with no requirement to URL encode
|
164
|
+
the characters in them.
|
165
|
+
EOF
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def validate_no_label_clashes!(registry)
|
170
|
+
# There's nothing to check if we don't have a grouping key
|
171
|
+
return if @grouping_key.empty?
|
172
|
+
|
173
|
+
# We could be doing a lot of comparisons, so let's do them against a
|
174
|
+
# set rather than an array
|
175
|
+
grouping_key_labels = @grouping_key.keys.to_set
|
176
|
+
|
177
|
+
registry.metrics.each do |metric|
|
178
|
+
metric.values.keys.first.keys.each do |label|
|
179
|
+
if grouping_key_labels.include?(label)
|
180
|
+
raise LabelSetValidator::InvalidLabelSetError,
|
181
|
+
"label :#{label} from grouping key collides with label of the " \
|
182
|
+
"same name from metric :#{metric.name} and would overwrite it"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def validate_response!(response)
|
189
|
+
status = Integer(response.code)
|
190
|
+
if status >= 300
|
191
|
+
message = "status: #{response.code}, message: #{response.message}, body: #{response.body}"
|
192
|
+
if status <= 399
|
193
|
+
raise HttpRedirectError, message
|
194
|
+
elsif status <= 499
|
195
|
+
raise HttpClientError, message
|
196
|
+
else
|
197
|
+
raise HttpServerError, message
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
93
201
|
end
|
94
202
|
end
|
95
203
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus-client-mmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schmidt
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-01-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fuzzbert
|
@@ -117,7 +117,9 @@ files:
|
|
117
117
|
- ext/fast_mmaped_file/value_access.c
|
118
118
|
- ext/fast_mmaped_file/value_access.h
|
119
119
|
- lib/fast_mmaped_file.bundle
|
120
|
+
- lib/mmap.rb
|
120
121
|
- lib/prometheus.rb
|
122
|
+
- lib/prometheus/#client.rb#
|
121
123
|
- lib/prometheus/client.rb
|
122
124
|
- lib/prometheus/client/configuration.rb
|
123
125
|
- lib/prometheus/client/counter.rb
|
@@ -174,14 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
174
176
|
requirements:
|
175
177
|
- - ">="
|
176
178
|
- !ruby/object:Gem::Version
|
177
|
-
version:
|
179
|
+
version: 2.7.0
|
178
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
181
|
requirements:
|
180
182
|
- - ">="
|
181
183
|
- !ruby/object:Gem::Version
|
182
184
|
version: '0'
|
183
185
|
requirements: []
|
184
|
-
rubygems_version: 3.3.
|
186
|
+
rubygems_version: 3.3.26
|
185
187
|
signing_key:
|
186
188
|
specification_version: 4
|
187
189
|
summary: A suite of instrumentation metric primitivesthat can be exposed through a
|