prometheus-client-mmap 0.22.0-x86_64-linux-musl → 1.2.1-x86_64-linux-musl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/README.md +32 -17
  4. data/ext/fast_mmaped_file_rs/Cargo.toml +14 -9
  5. data/ext/fast_mmaped_file_rs/build.rs +5 -0
  6. data/ext/fast_mmaped_file_rs/extconf.rb +1 -3
  7. data/ext/fast_mmaped_file_rs/src/error.rs +2 -2
  8. data/ext/fast_mmaped_file_rs/src/file_entry.rs +222 -17
  9. data/ext/fast_mmaped_file_rs/src/file_info.rs +56 -6
  10. data/ext/fast_mmaped_file_rs/src/lib.rs +0 -1
  11. data/ext/fast_mmaped_file_rs/src/map.rs +12 -12
  12. data/ext/fast_mmaped_file_rs/src/mmap/inner.rs +6 -10
  13. data/ext/fast_mmaped_file_rs/src/mmap.rs +225 -47
  14. data/ext/fast_mmaped_file_rs/src/raw_entry.rs +1 -1
  15. data/ext/fast_mmaped_file_rs/src/testhelper.rs +1 -1
  16. data/lib/3.1/fast_mmaped_file_rs.so +0 -0
  17. data/lib/3.2/fast_mmaped_file_rs.so +0 -0
  18. data/lib/3.3/fast_mmaped_file_rs.so +0 -0
  19. data/lib/3.4/fast_mmaped_file_rs.so +0 -0
  20. data/lib/prometheus/client/formats/text.rb +1 -34
  21. data/lib/prometheus/client/helper/mmaped_file.rb +3 -3
  22. data/lib/prometheus/client/label_set_validator.rb +1 -2
  23. data/lib/prometheus/client/support/puma.rb +44 -0
  24. data/lib/prometheus/client/version.rb +1 -1
  25. metadata +62 -61
  26. data/ext/fast_mmaped_file/extconf.rb +0 -30
  27. data/ext/fast_mmaped_file/fast_mmaped_file.c +0 -122
  28. data/ext/fast_mmaped_file/file_format.c +0 -5
  29. data/ext/fast_mmaped_file/file_format.h +0 -11
  30. data/ext/fast_mmaped_file/file_parsing.c +0 -195
  31. data/ext/fast_mmaped_file/file_parsing.h +0 -27
  32. data/ext/fast_mmaped_file/file_reading.c +0 -102
  33. data/ext/fast_mmaped_file/file_reading.h +0 -30
  34. data/ext/fast_mmaped_file/globals.h +0 -14
  35. data/ext/fast_mmaped_file/mmap.c +0 -427
  36. data/ext/fast_mmaped_file/mmap.h +0 -61
  37. data/ext/fast_mmaped_file/rendering.c +0 -199
  38. data/ext/fast_mmaped_file/rendering.h +0 -8
  39. data/ext/fast_mmaped_file/utils.c +0 -56
  40. data/ext/fast_mmaped_file/utils.h +0 -22
  41. data/ext/fast_mmaped_file/value_access.c +0 -242
  42. data/ext/fast_mmaped_file/value_access.h +0 -15
  43. data/ext/fast_mmaped_file_rs/.cargo/config.toml +0 -23
  44. data/ext/fast_mmaped_file_rs/Cargo.lock +0 -790
  45. data/ext/fast_mmaped_file_rs/src/parser.rs +0 -346
  46. data/lib/2.7/fast_mmaped_file.so +0 -0
  47. data/lib/2.7/fast_mmaped_file_rs.so +0 -0
  48. data/lib/3.0/fast_mmaped_file.so +0 -0
  49. data/lib/3.0/fast_mmaped_file_rs.so +0 -0
  50. data/lib/3.1/fast_mmaped_file.so +0 -0
  51. data/lib/3.2/fast_mmaped_file.so +0 -0
  52. data/vendor/c/hashmap/.gitignore +0 -52
  53. data/vendor/c/hashmap/LICENSE +0 -21
  54. data/vendor/c/hashmap/README.md +0 -90
  55. data/vendor/c/hashmap/_config.yml +0 -1
  56. data/vendor/c/hashmap/src/hashmap.c +0 -692
  57. data/vendor/c/hashmap/src/hashmap.h +0 -267
  58. data/vendor/c/hashmap/test/Makefile +0 -22
  59. data/vendor/c/hashmap/test/hashmap_test.c +0 -608
  60. data/vendor/c/jsmn/.travis.yml +0 -4
  61. data/vendor/c/jsmn/LICENSE +0 -20
  62. data/vendor/c/jsmn/Makefile +0 -41
  63. data/vendor/c/jsmn/README.md +0 -168
  64. data/vendor/c/jsmn/example/jsondump.c +0 -126
  65. data/vendor/c/jsmn/example/simple.c +0 -76
  66. data/vendor/c/jsmn/jsmn.c +0 -314
  67. data/vendor/c/jsmn/jsmn.h +0 -76
  68. data/vendor/c/jsmn/library.json +0 -16
  69. data/vendor/c/jsmn/test/test.h +0 -27
  70. data/vendor/c/jsmn/test/tests.c +0 -407
  71. data/vendor/c/jsmn/test/testutil.h +0 -94
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7413feb908630e80a9949470f32d3d93ad796b50e67dbdf4c601ff3254fc442e
4
- data.tar.gz: 9f7d05d315e5ef4a4b776fd9e704599074756a8876bb2b22a44960571bda51d4
3
+ metadata.gz: 789dc7485bf3e87ace45cd1c90f63499f8edec0d01526a99198da98260b7b87c
4
+ data.tar.gz: 74b69f87d4401ba7027197ca74d5ad512413bf4f05224c552d4b4781b64dd1ac
5
5
  SHA512:
6
- metadata.gz: 524070df387d7dbb72cf119715106b5586794263d433ed71b0e8da75a67147eeeddf365b587a6d9b13d26058d70600925ba037c2130bf87634b68baa06b4f52f
7
- data.tar.gz: dcbb9b4f11ba3cd9ac67682c6a8f4462a920ff73b14f6578cd51c75d461cdd3cbcf433a5a1b21398e3c3bbb81131af5b9d77023adf50ba64a13fadb1e530181b
6
+ metadata.gz: dbbbf7a942c902dd3d884e3ff4e50212c5f0fa8f366b827e2ace2d372308f673a9e7557ea5e8f1c821241b698fbcd8aa1efdc72f106cea276dfd4f000fe35ab9
7
+ data.tar.gz: eb0a13f5f47cbc321504d6c20dcd096151dd52950f54e4a006dc7e5bf301a34881ef523bf11a56e0eab93b76f91a42efe309c69246ceae984ffee4892413893a
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
- [![Dependency Status][5]](https://gemnasium.com/prometheus/prometheus-client-mmap)
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 (experimental)
54
+ ## Rust extension
38
55
 
39
- In an effort to improve maintainability, there is now an optional Rust
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. If `rustc` is available, then the Rust extension will
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 can
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
- To use it add this line to your `configure` block:
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
- ###`bin/parse`
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.13"
9
+ hashbrown = "0.14"
10
10
  libc = "0.2"
11
- magnus = { git = "https://github.com/matsadler/magnus", branch = "main", features = ["rb-sys"] }
12
- memmap2 = "0.5"
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
- smallvec = "1.10"
17
- thiserror = "1.0"
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.4"
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 = { git = "https://github.com/matsadler/magnus", branch = "main", features = ["rb-sys","embed"] }
25
+ magnus = { version = "0.7", features = ["rb-sys","embed"] }
24
26
  rand = "0.8"
25
27
  sha2 = "0.10"
26
- tempfile = "3.5"
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`.
@@ -0,0 +1,5 @@
1
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
2
+ let _ = rb_sys_env::activate()?;
3
+
4
+ Ok(())
5
+ }
@@ -24,7 +24,5 @@ if find_executable('rustc')
24
24
  ARGV = trimmed_argv
25
25
  end
26
26
  else
27
- puts 'rustc not found, skipping Rust extension.'
28
-
29
- File.write('Makefile', dummy_makefile($srcdir).join(''))
27
+ raise 'rustc not found. prometheus-client-mmap now requires Rust.'
30
28
  end
@@ -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::{StaticSymbol, Symbol};
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_: StaticSymbol,
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 parser::parse_metrics(&entry.data.json) {
138
- Some(m) => m,
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
- None => continue,
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, &val)) in it.enumerate() {
217
+ for (i, (&key, val)) in it.enumerate() {
201
218
  out.push_str(key);
202
-
203
- out.push_str("=\"");
204
-
205
- // `null` values will be output as `""`.
206
- if val != "null" {
207
- out.push_str(val);
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_: StaticSymbol::new("gauge"),
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_: StaticSymbol::new(case.metric_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, StaticSymbol, Symbol, Value};
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_: StaticSymbol,
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_ = StaticSymbol::from_value(params[2])
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, StaticSymbol, Symbol};
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_: StaticSymbol::new("max"),
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
  }
@@ -12,7 +12,6 @@ pub mod file_info;
12
12
  mod macros;
13
13
  pub mod map;
14
14
  pub mod mmap;
15
- pub mod parser;
16
15
  pub mod raw_entry;
17
16
  pub mod util;
18
17