prometheus-client-mmap 0.22.0-x86_64-linux-musl → 1.2.4-x86_64-linux-musl
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.tool-versions +1 -0
- data/README.md +32 -17
- data/ext/fast_mmaped_file_rs/Cargo.toml +14 -9
- data/ext/fast_mmaped_file_rs/build.rs +5 -0
- data/ext/fast_mmaped_file_rs/extconf.rb +1 -3
- data/ext/fast_mmaped_file_rs/src/error.rs +2 -2
- data/ext/fast_mmaped_file_rs/src/file_entry.rs +222 -17
- data/ext/fast_mmaped_file_rs/src/file_info.rs +56 -6
- data/ext/fast_mmaped_file_rs/src/lib.rs +0 -1
- data/ext/fast_mmaped_file_rs/src/map.rs +12 -12
- data/ext/fast_mmaped_file_rs/src/mmap/inner.rs +6 -10
- data/ext/fast_mmaped_file_rs/src/mmap.rs +225 -47
- data/ext/fast_mmaped_file_rs/src/raw_entry.rs +1 -1
- data/ext/fast_mmaped_file_rs/src/testhelper.rs +1 -1
- data/lib/3.1/fast_mmaped_file_rs.so +0 -0
- data/lib/3.2/fast_mmaped_file_rs.so +0 -0
- data/lib/3.3/fast_mmaped_file_rs.so +0 -0
- data/lib/3.4/fast_mmaped_file_rs.so +0 -0
- data/lib/prometheus/client/formats/text.rb +1 -34
- data/lib/prometheus/client/helper/mmaped_file.rb +3 -3
- data/lib/prometheus/client/label_set_validator.rb +1 -2
- data/lib/prometheus/client/support/puma.rb +44 -0
- data/lib/prometheus/client/version.rb +1 -1
- metadata +62 -61
- data/ext/fast_mmaped_file/extconf.rb +0 -30
- data/ext/fast_mmaped_file/fast_mmaped_file.c +0 -122
- data/ext/fast_mmaped_file/file_format.c +0 -5
- data/ext/fast_mmaped_file/file_format.h +0 -11
- data/ext/fast_mmaped_file/file_parsing.c +0 -195
- data/ext/fast_mmaped_file/file_parsing.h +0 -27
- data/ext/fast_mmaped_file/file_reading.c +0 -102
- data/ext/fast_mmaped_file/file_reading.h +0 -30
- data/ext/fast_mmaped_file/globals.h +0 -14
- data/ext/fast_mmaped_file/mmap.c +0 -427
- data/ext/fast_mmaped_file/mmap.h +0 -61
- data/ext/fast_mmaped_file/rendering.c +0 -199
- data/ext/fast_mmaped_file/rendering.h +0 -8
- data/ext/fast_mmaped_file/utils.c +0 -56
- data/ext/fast_mmaped_file/utils.h +0 -22
- data/ext/fast_mmaped_file/value_access.c +0 -242
- data/ext/fast_mmaped_file/value_access.h +0 -15
- data/ext/fast_mmaped_file_rs/.cargo/config.toml +0 -23
- data/ext/fast_mmaped_file_rs/Cargo.lock +0 -790
- data/ext/fast_mmaped_file_rs/src/parser.rs +0 -346
- data/lib/2.7/fast_mmaped_file.so +0 -0
- data/lib/2.7/fast_mmaped_file_rs.so +0 -0
- data/lib/3.0/fast_mmaped_file.so +0 -0
- data/lib/3.0/fast_mmaped_file_rs.so +0 -0
- data/lib/3.1/fast_mmaped_file.so +0 -0
- data/lib/3.2/fast_mmaped_file.so +0 -0
- data/vendor/c/hashmap/.gitignore +0 -52
- data/vendor/c/hashmap/LICENSE +0 -21
- data/vendor/c/hashmap/README.md +0 -90
- data/vendor/c/hashmap/_config.yml +0 -1
- data/vendor/c/hashmap/src/hashmap.c +0 -692
- data/vendor/c/hashmap/src/hashmap.h +0 -267
- data/vendor/c/hashmap/test/Makefile +0 -22
- data/vendor/c/hashmap/test/hashmap_test.c +0 -608
- data/vendor/c/jsmn/.travis.yml +0 -4
- data/vendor/c/jsmn/LICENSE +0 -20
- data/vendor/c/jsmn/Makefile +0 -41
- data/vendor/c/jsmn/README.md +0 -168
- data/vendor/c/jsmn/example/jsondump.c +0 -126
- data/vendor/c/jsmn/example/simple.c +0 -76
- data/vendor/c/jsmn/jsmn.c +0 -314
- data/vendor/c/jsmn/jsmn.h +0 -76
- data/vendor/c/jsmn/library.json +0 -16
- data/vendor/c/jsmn/test/test.h +0 -27
- data/vendor/c/jsmn/test/tests.c +0 -407
- data/vendor/c/jsmn/test/testutil.h +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c63779d5054a6bb9fa12ac55091ab5fd477687c05f9b881d2936728495c0b6ae
|
4
|
+
data.tar.gz: 4a37d68b5a001c553015c98ce53aeb70972a1a0e28588ac5f26e7d73cf9b05ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b1e9b8d51b52df3c39b68a6e33461e67849f053fe0f2a18ce8a6ea136891ece7ac679581ead38024e280ea5975bec6c93fa785a26f22cba188d71b0d01a862b
|
7
|
+
data.tar.gz: ed675bfd2aa98cdeedef223186ab6c376e9831d93b3c57efb1f9a3d1c3890e1ee423a2dd324c8827e1367051ce37ed6a67e0a620fee21b1ce471e8fd66f2f32e
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rust 1.73.0
|
data/README.md
CHANGED
@@ -10,7 +10,24 @@ through a HTTP interface. Intended to be used together with a
|
|
10
10
|
|
11
11
|
[![Gem Version][4]](http://badge.fury.io/rb/prometheus-client-mmap)
|
12
12
|
[![Build Status][3]](https://gitlab.com/gitlab-org/prometheus-client-mmap/commits/master)
|
13
|
-
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
prometheus-client-mmap ships with [precompiled native gems](https://rubygems.org/gems/prometheus-client-mmap).
|
17
|
+
Install the gem via:
|
18
|
+
|
19
|
+
```shell
|
20
|
+
gem install prometheus-client-mmap
|
21
|
+
```
|
22
|
+
|
23
|
+
### Requirements for building from source
|
24
|
+
|
25
|
+
Building from source requires a number of dependencies:
|
26
|
+
|
27
|
+
* Rust v1.65+
|
28
|
+
* clang 5.0 or higher for [bindgen](https://rust-lang.github.io/rust-bindgen/requirements.html)
|
29
|
+
* `make`
|
30
|
+
* A C compiler (for legacy extension, which will be removed soon)
|
14
31
|
|
15
32
|
## Usage
|
16
33
|
|
@@ -34,19 +51,11 @@ http_requests = prometheus.counter(:http_requests, 'A counter of HTTP requests m
|
|
34
51
|
http_requests.increment
|
35
52
|
```
|
36
53
|
|
37
|
-
## Rust extension
|
54
|
+
## Rust extension
|
38
55
|
|
39
|
-
|
56
|
+
This gem now uses a rewritten Rust extension instead of C.
|
40
57
|
implementation that reads the metric files and outputs the multiprocess
|
41
|
-
metrics to text.
|
42
|
-
be built automatically. The `use_rust` keyword argument can be used:
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
puts Prometheus::Client::Formats::Text.marshal_multiprocess(use_rust: true)
|
46
|
-
```
|
47
|
-
|
48
|
-
Note that this parameter will likely be deprecated and removed once the Rust
|
49
|
-
extension becomes the default mode.
|
58
|
+
metrics to text. This implementation is significantly faster than the C extension.
|
50
59
|
|
51
60
|
### Rack middleware
|
52
61
|
|
@@ -204,8 +213,8 @@ prometheus_multiproc_dir=/tmp
|
|
204
213
|
|
205
214
|
### PID cardinality
|
206
215
|
|
207
|
-
In multiprocess setup e.g. running under Unicorn, having worker process restart often
|
208
|
-
lead to performance problems when proccesing metric files. By default each process using
|
216
|
+
In multiprocess setup e.g. running under Unicorn or Puma, having worker process restart often
|
217
|
+
can lead to performance problems when proccesing metric files. By default each process using
|
209
218
|
Prometheus metrics will create a set of files based on that process PID. With high worker
|
210
219
|
churn this will lead to creation of thousands of files and in turn will cause very noticable
|
211
220
|
slowdown when displaying metrics
|
@@ -213,17 +222,23 @@ slowdown when displaying metrics
|
|
213
222
|
To reduce this problem, a surrogate process id can be used. Set of all such IDs needs
|
214
223
|
have low cardinality, and each process id must be unique among all running process.
|
215
224
|
|
216
|
-
For Unicorn a worker id/number can be used to greatly speedup the metrics rendering.
|
225
|
+
For Unicorn and Puma a worker id/number can be used to greatly speedup the metrics rendering.
|
217
226
|
|
218
|
-
|
227
|
+
If you are using Unicorn, add this line to your `configure` block:
|
219
228
|
|
220
229
|
```ruby
|
221
230
|
config.pid_provider = Prometheus::Client::Support::Unicorn.method(:worker_pid_provider)
|
222
231
|
```
|
223
232
|
|
233
|
+
If you are using Puma, add this line to your `configure` block:
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
config.pid_provider = Prometheus::Client::Support::Puma.method(:worker_pid_provider)
|
237
|
+
```
|
238
|
+
|
224
239
|
## Tools
|
225
240
|
|
226
|
-
|
241
|
+
### `bin/parse`
|
227
242
|
|
228
243
|
This command can be used to parse metric files located on the filesystem just like a metric exporter would.
|
229
244
|
It outputs either `json` formatted raw data or digested data in prometheus `text` format.
|
@@ -6,24 +6,29 @@ edition = "2021"
|
|
6
6
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
7
7
|
|
8
8
|
[dependencies]
|
9
|
-
hashbrown = "0.
|
9
|
+
hashbrown = "0.14"
|
10
10
|
libc = "0.2"
|
11
|
-
magnus = {
|
12
|
-
memmap2 = "0.
|
11
|
+
magnus = { version = "0.7", features = ["rb-sys"] }
|
12
|
+
memmap2 = "0.9"
|
13
13
|
# v0.26 cannot be built on CentOS 7 https://github.com/nix-rust/nix/issues/1972
|
14
14
|
nix = { version = "0.25", features = ["mman"] } # mman used for MsFlags
|
15
|
-
rb-sys = "0.9"
|
16
|
-
|
17
|
-
|
15
|
+
rb-sys = { version = "0.9", features = ["stable-api-compiled-fallback"] }
|
16
|
+
serde = { version = "1.0", features = ["derive"] }
|
17
|
+
serde_json = { version = "1.0", features = ["raw_value"] }
|
18
|
+
smallvec = { version = "1.13", features = ["serde"] }
|
19
|
+
thiserror = "2.0"
|
18
20
|
|
19
21
|
[dev-dependencies]
|
20
|
-
bstr = "1.
|
22
|
+
bstr = "1.11"
|
21
23
|
indoc = "2.0"
|
22
24
|
# We need the `embed` feature to run tests, but this triggers failures when building as a Gem.
|
23
|
-
magnus = {
|
25
|
+
magnus = { version = "0.7", features = ["rb-sys","embed"] }
|
24
26
|
rand = "0.8"
|
25
27
|
sha2 = "0.10"
|
26
|
-
tempfile = "3.
|
28
|
+
tempfile = "3.14"
|
29
|
+
|
30
|
+
[build-dependencies]
|
31
|
+
rb-sys-env = { git = "https://github.com/oxidize-rb/rb-sys.git", rev = "5d1999ba70d301581524b8252c8ff3b8a825afa2" }
|
27
32
|
|
28
33
|
[lib]
|
29
34
|
# Integration tests won't work if crate is only `cdylib`.
|
@@ -9,7 +9,7 @@ use crate::util;
|
|
9
9
|
use crate::PROM_EPARSING_ERROR;
|
10
10
|
|
11
11
|
/// A lightweight representation of Ruby ExceptionClasses.
|
12
|
-
#[derive(PartialEq, Clone, Copy, Debug)]
|
12
|
+
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
13
13
|
pub enum RubyError {
|
14
14
|
Arg,
|
15
15
|
Encoding,
|
@@ -45,7 +45,7 @@ impl From<RubyError> for magnus::ExceptionClass {
|
|
45
45
|
/// Errors returned internally within the crate. Methods called directly by Ruby return
|
46
46
|
/// `magnus::error::Error` as do functions that interact heavily with Ruby. This can be
|
47
47
|
/// converted into a `magnus::error::Error` at the boundary between Rust and Ruby.
|
48
|
-
#[derive(PartialEq, Error, Debug)]
|
48
|
+
#[derive(PartialEq, Eq, Error, Debug)]
|
49
49
|
pub enum MmapError {
|
50
50
|
/// A read or write was made while another thread had mutable access to the mmap.
|
51
51
|
#[error("read/write operation attempted while mmap was being written to")]
|
@@ -1,10 +1,12 @@
|
|
1
|
-
use magnus::
|
1
|
+
use magnus::Symbol;
|
2
|
+
use serde::Deserialize;
|
3
|
+
use serde_json::value::RawValue;
|
4
|
+
use smallvec::SmallVec;
|
2
5
|
use std::fmt::Write;
|
3
6
|
use std::str;
|
4
7
|
|
5
8
|
use crate::error::{MmapError, RubyError};
|
6
9
|
use crate::file_info::FileInfo;
|
7
|
-
use crate::parser::{self, MetricText};
|
8
10
|
use crate::raw_entry::RawEntry;
|
9
11
|
use crate::Result;
|
10
12
|
use crate::{SYM_GAUGE, SYM_LIVESUM, SYM_MAX, SYM_MIN};
|
@@ -16,6 +18,16 @@ pub struct FileEntry {
|
|
16
18
|
pub meta: EntryMetadata,
|
17
19
|
}
|
18
20
|
|
21
|
+
/// String slices pointing to the fields of a borrowed `Entry`'s JSON data.
|
22
|
+
#[derive(Deserialize, Debug)]
|
23
|
+
pub struct MetricText<'a> {
|
24
|
+
pub family_name: &'a str,
|
25
|
+
pub metric_name: &'a str,
|
26
|
+
pub labels: SmallVec<[&'a str; 4]>,
|
27
|
+
#[serde(borrow)]
|
28
|
+
pub values: SmallVec<[&'a RawValue; 4]>,
|
29
|
+
}
|
30
|
+
|
19
31
|
/// The primary data payload for a `FileEntry`, the JSON string and the
|
20
32
|
/// associated pid, if significant. Used as the key for `EntryMap`.
|
21
33
|
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
@@ -80,7 +92,7 @@ impl<'a> BorrowedData<'a> {
|
|
80
92
|
#[derive(Clone, Debug)]
|
81
93
|
pub struct EntryMetadata {
|
82
94
|
pub multiprocess_mode: Symbol,
|
83
|
-
pub type_:
|
95
|
+
pub type_: Symbol,
|
84
96
|
pub value: f64,
|
85
97
|
}
|
86
98
|
|
@@ -134,11 +146,16 @@ impl FileEntry {
|
|
134
146
|
let mut processed_count = 0;
|
135
147
|
|
136
148
|
for entry in entries {
|
137
|
-
let metrics_data = match
|
138
|
-
|
149
|
+
let metrics_data = match serde_json::from_str::<MetricText>(&entry.data.json) {
|
150
|
+
Ok(m) => {
|
151
|
+
if m.labels.len() != m.values.len() {
|
152
|
+
continue;
|
153
|
+
}
|
154
|
+
m
|
155
|
+
}
|
139
156
|
// We don't exit the function here so the total number of invalid
|
140
157
|
// entries can be calculated below.
|
141
|
-
|
158
|
+
Err(_) => continue,
|
142
159
|
};
|
143
160
|
|
144
161
|
match prev_name.as_ref() {
|
@@ -176,7 +193,7 @@ impl FileEntry {
|
|
176
193
|
out.push_str(family_name);
|
177
194
|
out.push(' ');
|
178
195
|
|
179
|
-
out.push_str(self.meta.type_.name().expect("name was invalid UTF-8"));
|
196
|
+
out.push_str(&self.meta.type_.name().expect("name was invalid UTF-8"));
|
180
197
|
out.push('\n');
|
181
198
|
}
|
182
199
|
|
@@ -197,16 +214,20 @@ impl FileEntry {
|
|
197
214
|
|
198
215
|
let it = json_data.labels.iter().zip(json_data.values.iter());
|
199
216
|
|
200
|
-
for (i, (&key,
|
217
|
+
for (i, (&key, val)) in it.enumerate() {
|
201
218
|
out.push_str(key);
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
219
|
+
out.push('=');
|
220
|
+
|
221
|
+
match val.get() {
|
222
|
+
"null" => out.push_str("\"\""),
|
223
|
+
s if s.starts_with('"') => out.push_str(s),
|
224
|
+
s => {
|
225
|
+
// Quote numeric values.
|
226
|
+
out.push('"');
|
227
|
+
out.push_str(s);
|
228
|
+
out.push('"');
|
229
|
+
}
|
208
230
|
}
|
209
|
-
out.push('"');
|
210
231
|
|
211
232
|
if i < json_data.labels.len() - 1 {
|
212
233
|
out.push(',');
|
@@ -276,6 +297,20 @@ mod test {
|
|
276
297
|
"##}),
|
277
298
|
expected_err: None,
|
278
299
|
},
|
300
|
+
TestCase {
|
301
|
+
name: "many labels",
|
302
|
+
multiprocess_mode: "min",
|
303
|
+
json: &[
|
304
|
+
r#"["family","name",["label_a","label_b","label_c","label_d","label_e"],["value_a","value_b","value_c","value_d","value_e"]]"#,
|
305
|
+
],
|
306
|
+
values: &[1.0],
|
307
|
+
pids: &["worker-1"],
|
308
|
+
expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
|
309
|
+
# TYPE family gauge
|
310
|
+
name{label_a="value_a",label_b="value_b",label_c="value_c",label_d="value_d",label_e="value_e"} 1
|
311
|
+
"##}),
|
312
|
+
expected_err: None,
|
313
|
+
},
|
279
314
|
TestCase {
|
280
315
|
name: "floating point shown",
|
281
316
|
multiprocess_mode: "min",
|
@@ -288,6 +323,44 @@ mod test {
|
|
288
323
|
"##}),
|
289
324
|
expected_err: None,
|
290
325
|
},
|
326
|
+
TestCase {
|
327
|
+
name: "numeric value",
|
328
|
+
multiprocess_mode: "min",
|
329
|
+
json: &[
|
330
|
+
r#"["family","name",["label_a","label_b","label_c"],["value_a",403,-0.2E5]]"#,
|
331
|
+
],
|
332
|
+
values: &[1.5],
|
333
|
+
pids: &["worker-1"],
|
334
|
+
expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
|
335
|
+
# TYPE family gauge
|
336
|
+
name{label_a="value_a",label_b="403",label_c="-0.2E5"} 1.5
|
337
|
+
"##}),
|
338
|
+
expected_err: None,
|
339
|
+
},
|
340
|
+
TestCase {
|
341
|
+
name: "null value",
|
342
|
+
multiprocess_mode: "min",
|
343
|
+
json: &[r#"["family","name",["label_a","label_b"],["value_a",null]]"#],
|
344
|
+
values: &[1.5],
|
345
|
+
pids: &["worker-1"],
|
346
|
+
expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
|
347
|
+
# TYPE family gauge
|
348
|
+
name{label_a="value_a",label_b=""} 1.5
|
349
|
+
"##}),
|
350
|
+
expected_err: None,
|
351
|
+
},
|
352
|
+
TestCase {
|
353
|
+
name: "comma in value",
|
354
|
+
multiprocess_mode: "min",
|
355
|
+
json: &[r#"["family","name",["label_a","label_b"],["value_a","value,_b"]]"#],
|
356
|
+
values: &[1.5],
|
357
|
+
pids: &["worker-1"],
|
358
|
+
expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
|
359
|
+
# TYPE family gauge
|
360
|
+
name{label_a="value_a",label_b="value,_b"} 1.5
|
361
|
+
"##}),
|
362
|
+
expected_err: None,
|
363
|
+
},
|
291
364
|
TestCase {
|
292
365
|
name: "no labels, pid significant",
|
293
366
|
multiprocess_mode: "all",
|
@@ -413,6 +486,138 @@ mod test {
|
|
413
486
|
RubyError::Runtime,
|
414
487
|
)),
|
415
488
|
},
|
489
|
+
TestCase {
|
490
|
+
name: "too many values",
|
491
|
+
multiprocess_mode: "min",
|
492
|
+
json: &[r#"["family","name",["label_a"],["value_a","value,_b"]]"#],
|
493
|
+
values: &[1.5],
|
494
|
+
pids: &["worker-1"],
|
495
|
+
expected_out: None,
|
496
|
+
expected_err: Some(MmapError::legacy(
|
497
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
498
|
+
RubyError::Runtime,
|
499
|
+
)),
|
500
|
+
},
|
501
|
+
TestCase {
|
502
|
+
name: "no values",
|
503
|
+
multiprocess_mode: "min",
|
504
|
+
json: &[r#"["family","name",["label_a"]]"#],
|
505
|
+
values: &[1.5],
|
506
|
+
pids: &["worker-1"],
|
507
|
+
expected_out: None,
|
508
|
+
expected_err: Some(MmapError::legacy(
|
509
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
510
|
+
RubyError::Runtime,
|
511
|
+
)),
|
512
|
+
},
|
513
|
+
TestCase {
|
514
|
+
name: "no labels or values",
|
515
|
+
multiprocess_mode: "min",
|
516
|
+
json: &[r#"["family","name","foo"]"#],
|
517
|
+
values: &[1.5],
|
518
|
+
pids: &["worker-1"],
|
519
|
+
expected_out: None,
|
520
|
+
expected_err: Some(MmapError::legacy(
|
521
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
522
|
+
RubyError::Runtime,
|
523
|
+
)),
|
524
|
+
},
|
525
|
+
TestCase {
|
526
|
+
name: "too many leading brackets",
|
527
|
+
multiprocess_mode: "min",
|
528
|
+
json: &[r#"[["family","name",["label_a","label_b"],["value_a","value_b"]]"#],
|
529
|
+
values: &[1.5],
|
530
|
+
pids: &["worker-1"],
|
531
|
+
expected_out: None,
|
532
|
+
expected_err: Some(MmapError::legacy(
|
533
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
534
|
+
RubyError::Runtime,
|
535
|
+
)),
|
536
|
+
},
|
537
|
+
TestCase {
|
538
|
+
name: "too many trailing brackets",
|
539
|
+
multiprocess_mode: "min",
|
540
|
+
json: &[r#"["family","name",["label_a","label_b"],["value_a","value_b"]]]"#],
|
541
|
+
values: &[1.5],
|
542
|
+
pids: &["worker-1"],
|
543
|
+
expected_out: None,
|
544
|
+
expected_err: Some(MmapError::legacy(
|
545
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
546
|
+
RubyError::Runtime,
|
547
|
+
)),
|
548
|
+
},
|
549
|
+
TestCase {
|
550
|
+
name: "too many leading label brackets",
|
551
|
+
multiprocess_mode: "min",
|
552
|
+
json: &[r#"["family","name",[["label_a","label_b"],["value_a","value_b"]]"#],
|
553
|
+
values: &[1.5],
|
554
|
+
pids: &["worker-1"],
|
555
|
+
expected_out: None,
|
556
|
+
expected_err: Some(MmapError::legacy(
|
557
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
558
|
+
RubyError::Runtime,
|
559
|
+
)),
|
560
|
+
},
|
561
|
+
TestCase {
|
562
|
+
name: "too many leading label brackets",
|
563
|
+
multiprocess_mode: "min",
|
564
|
+
json: &[r#"["family","name",[["label_a","label_b"],["value_a","value_b"]]"#],
|
565
|
+
values: &[1.5],
|
566
|
+
pids: &["worker-1"],
|
567
|
+
expected_out: None,
|
568
|
+
expected_err: Some(MmapError::legacy(
|
569
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
570
|
+
RubyError::Runtime,
|
571
|
+
)),
|
572
|
+
},
|
573
|
+
TestCase {
|
574
|
+
name: "too many leading value brackets",
|
575
|
+
multiprocess_mode: "min",
|
576
|
+
json: &[r#"["family","name",["label_a","label_b"],[["value_a","value_b"]]"#],
|
577
|
+
values: &[1.5],
|
578
|
+
pids: &["worker-1"],
|
579
|
+
expected_out: None,
|
580
|
+
expected_err: Some(MmapError::legacy(
|
581
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
582
|
+
RubyError::Runtime,
|
583
|
+
)),
|
584
|
+
},
|
585
|
+
TestCase {
|
586
|
+
name: "misplaced bracket",
|
587
|
+
multiprocess_mode: "min",
|
588
|
+
json: &[r#"["family","name",["label_a","label_b"],]["value_a","value_b"]]"#],
|
589
|
+
values: &[1.5],
|
590
|
+
pids: &["worker-1"],
|
591
|
+
expected_out: None,
|
592
|
+
expected_err: Some(MmapError::legacy(
|
593
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
594
|
+
RubyError::Runtime,
|
595
|
+
)),
|
596
|
+
},
|
597
|
+
TestCase {
|
598
|
+
name: "comma in numeric",
|
599
|
+
multiprocess_mode: "min",
|
600
|
+
json: &[r#"["family","name",["label_a","label_b"],["value_a",403,0]]"#],
|
601
|
+
values: &[1.5],
|
602
|
+
pids: &["worker-1"],
|
603
|
+
expected_out: None,
|
604
|
+
expected_err: Some(MmapError::legacy(
|
605
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
606
|
+
RubyError::Runtime,
|
607
|
+
)),
|
608
|
+
},
|
609
|
+
TestCase {
|
610
|
+
name: "non-e letter in numeric",
|
611
|
+
multiprocess_mode: "min",
|
612
|
+
json: &[r#"["family","name",["label_a","label_b"],["value_a",-2.0c5]]"#],
|
613
|
+
values: &[1.5],
|
614
|
+
pids: &["worker-1"],
|
615
|
+
expected_out: None,
|
616
|
+
expected_err: Some(MmapError::legacy(
|
617
|
+
"Processed entries 0 != map entries 1".to_owned(),
|
618
|
+
RubyError::Runtime,
|
619
|
+
)),
|
620
|
+
},
|
416
621
|
];
|
417
622
|
|
418
623
|
for case in tc {
|
@@ -438,7 +643,7 @@ mod test {
|
|
438
643
|
path,
|
439
644
|
len: case.json.len(),
|
440
645
|
multiprocess_mode: Symbol::new(case.multiprocess_mode),
|
441
|
-
type_:
|
646
|
+
type_: Symbol::new("gauge"),
|
442
647
|
pid: pid.to_string(),
|
443
648
|
};
|
444
649
|
file_infos.push(info);
|
@@ -544,7 +749,7 @@ mod test {
|
|
544
749
|
path,
|
545
750
|
len: json.len(),
|
546
751
|
multiprocess_mode: Symbol::new(case.multiprocess_mode),
|
547
|
-
type_:
|
752
|
+
type_: Symbol::new(case.metric_type),
|
548
753
|
pid: "worker-1".to_string(),
|
549
754
|
};
|
550
755
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
use magnus::exception::*;
|
2
|
-
use magnus::{Error, RString,
|
2
|
+
use magnus::{Error, RString, Symbol, Value};
|
3
3
|
use std::ffi::OsString;
|
4
4
|
use std::fs::File;
|
5
5
|
use std::io::{self, Read, Seek};
|
@@ -18,7 +18,7 @@ pub struct FileInfo {
|
|
18
18
|
pub path: PathBuf,
|
19
19
|
pub len: usize,
|
20
20
|
pub multiprocess_mode: Symbol,
|
21
|
-
pub type_:
|
21
|
+
pub type_: Symbol,
|
22
22
|
pub pid: String,
|
23
23
|
}
|
24
24
|
|
@@ -59,7 +59,7 @@ impl FileInfo {
|
|
59
59
|
let multiprocess_mode = Symbol::from_value(params[1])
|
60
60
|
.ok_or_else(|| err!(arg_error(), "expected multiprocess_mode to be a symbol"))?;
|
61
61
|
|
62
|
-
let type_ =
|
62
|
+
let type_ = Symbol::from_value(params[2])
|
63
63
|
.ok_or_else(|| err!(arg_error(), "expected file type to be a symbol"))?;
|
64
64
|
|
65
65
|
let pid = RString::from_value(params[3])
|
@@ -91,6 +91,11 @@ impl FileInfo {
|
|
91
91
|
|
92
92
|
match self.file.read_to_end(buf) {
|
93
93
|
Ok(n) if n == self.len => Ok(()),
|
94
|
+
// A worker may expand the file between our `stat` and `read`, no harm done.
|
95
|
+
Ok(n) if n > self.len => {
|
96
|
+
self.len = n;
|
97
|
+
Ok(())
|
98
|
+
}
|
94
99
|
Ok(_) => Err(MmapError::io(
|
95
100
|
"read",
|
96
101
|
&self.path,
|
@@ -103,9 +108,11 @@ impl FileInfo {
|
|
103
108
|
|
104
109
|
#[cfg(test)]
|
105
110
|
mod test {
|
106
|
-
use magnus::{eval, RArray,
|
111
|
+
use magnus::{eval, RArray, Symbol};
|
107
112
|
use rand::{thread_rng, Rng};
|
108
113
|
use sha2::{Digest, Sha256};
|
114
|
+
use std::fs;
|
115
|
+
use std::io::Write;
|
109
116
|
|
110
117
|
use super::*;
|
111
118
|
use crate::testhelper::TestFile;
|
@@ -165,10 +172,10 @@ mod test {
|
|
165
172
|
|
166
173
|
let mut info = FileInfo {
|
167
174
|
file,
|
168
|
-
path,
|
175
|
+
path: path.clone(),
|
169
176
|
len: buf.len(),
|
170
177
|
multiprocess_mode: Symbol::new("puma"),
|
171
|
-
type_:
|
178
|
+
type_: Symbol::new("max"),
|
172
179
|
pid: "worker-0_0".to_string(),
|
173
180
|
};
|
174
181
|
|
@@ -187,4 +194,47 @@ mod test {
|
|
187
194
|
|
188
195
|
assert_eq!(in_hash, out_hash, "content hashes");
|
189
196
|
}
|
197
|
+
|
198
|
+
#[test]
|
199
|
+
fn test_read_from_file_resized() {
|
200
|
+
let _cleanup = unsafe { magnus::embed::init() };
|
201
|
+
let ruby = magnus::Ruby::get().unwrap();
|
202
|
+
crate::init(&ruby).unwrap();
|
203
|
+
|
204
|
+
const BUF_LEN: usize = 1 << 14; // 16KiB
|
205
|
+
|
206
|
+
// Create a buffer with random data.
|
207
|
+
let mut buf = vec![0u8; BUF_LEN];
|
208
|
+
thread_rng().fill(buf.as_mut_slice());
|
209
|
+
|
210
|
+
let TestFile {
|
211
|
+
file,
|
212
|
+
path,
|
213
|
+
dir: _dir,
|
214
|
+
} = TestFile::new(&buf);
|
215
|
+
|
216
|
+
let mut info = FileInfo {
|
217
|
+
file,
|
218
|
+
path: path.clone(),
|
219
|
+
len: buf.len(),
|
220
|
+
multiprocess_mode: Symbol::new("puma"),
|
221
|
+
type_: Symbol::new("max"),
|
222
|
+
pid: "worker-0_0".to_string(),
|
223
|
+
};
|
224
|
+
|
225
|
+
let mut resized_file = fs::OpenOptions::new()
|
226
|
+
.write(true)
|
227
|
+
.append(true)
|
228
|
+
.open(path)
|
229
|
+
.unwrap();
|
230
|
+
|
231
|
+
// Write data to file after it has been `stat`ed in the
|
232
|
+
// constructor.
|
233
|
+
resized_file.write_all(&[1; 1024]).unwrap();
|
234
|
+
|
235
|
+
let mut out_buf = Vec::new();
|
236
|
+
info.read_from_file(&mut out_buf).unwrap();
|
237
|
+
|
238
|
+
assert_eq!(BUF_LEN + 1024, info.len, "resized file updated len");
|
239
|
+
}
|
190
240
|
}
|