prometheus-client-mmap 0.24.4-x86_64-linux → 0.25.0-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 141d4232e008441a87bf9379f2c05a22e745278dd20d319a9ce7e25782293487
4
- data.tar.gz: 0572e1ed26fb7a1d2cdb63df84407ed845fa5f278610167972039344b0477976
3
+ metadata.gz: 8392f2d676133039741efc162c8365a692a8dd3ce2d584af4a97f40ed39981db
4
+ data.tar.gz: '01139643ee538a1c765f86cc54c2ab36a72282d6484378ba713041f30385ece8'
5
5
  SHA512:
6
- metadata.gz: b6b77d80551ca1accaef527fe55147efc97f2244503bfb5575092208c3234433e13013673f144802535e8f09c5ca7933f00ab0539d38503c587da5a7f495294c
7
- data.tar.gz: b6954547ccff1ff8224dbf9841393f4c929c1938402a85fa57ddcfaac1c9f3bfd8145406011bcd17d20d6a9f8d097ef947bd59c6774673ec2bb1fa8590be9ec4
6
+ metadata.gz: 9bace69a5e6398cc35ee32aad8b0ca99229e6e5313b6a9bb4f54aa767c24ddef2780c89759ce17586732c82d456782a53818dae8d9cf459962c894324f690b3e
7
+ data.tar.gz: 6b13dc1cad92df550e802addaceec0891d720ee602b1083cc8e82cd35523b3ca74614c171fe13300992b342fba67bc16374718629a794246031f76a6cf0f7ce3
@@ -3,6 +3,7 @@
3
3
  #include <errno.h>
4
4
  #include <fcntl.h>
5
5
  #include <ruby/util.h>
6
+ #include <ruby/version.h>
6
7
  #include <sys/mman.h>
7
8
 
8
9
  #include "file_format.h"
@@ -27,6 +28,16 @@
27
28
  */
28
29
  static VALUE weak_obj_tracker_get_key(VALUE val) { return val; }
29
30
 
31
+ static void update_rstring_len(VALUE val, size_t len) {
32
+ #if defined(RUBY_API_VERSION_MAJOR) && defined(RUBY_API_VERSION_MINOR) && (RUBY_API_VERSION_MAJOR == 3) && \
33
+ (RUBY_API_VERSION_MINOR >= 3)
34
+
35
+ RSTRING(val)->len = len;
36
+ #else
37
+ RSTRING(val)->as.heap.len = len;
38
+ #endif
39
+ }
40
+
30
41
  /**
31
42
  * Adds a T_STRING type to the WeakMap. The WeakMap should be stored
32
43
  * as an instance variable.
@@ -52,7 +63,7 @@ VALUE mm_update_obj_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, self)) {
52
63
  GET_MMAP(self, i_mm, MM_MODIFY);
53
64
 
54
65
  RSTRING(i)->as.heap.ptr = i_mm->t->addr;
55
- RSTRING(i)->as.heap.len = i_mm->t->real;
66
+ update_rstring_len(i, i_mm->t->real);
56
67
 
57
68
  return Qtrue;
58
69
  }
@@ -98,7 +109,7 @@ static VALUE mm_str(VALUE obj, int modify) {
98
109
  ret = rb_obj_alloc(rb_cString);
99
110
  RSTRING(ret)->as.heap.ptr = i_mm->t->addr;
100
111
  RSTRING(ret)->as.heap.aux.capa = i_mm->t->len;
101
- RSTRING(ret)->as.heap.len = i_mm->t->real;
112
+ update_rstring_len(ret, i_mm->t->real);
102
113
 
103
114
  weak_obj_tracker_add(obj, ret);
104
115
 
@@ -30,9 +30,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
30
30
 
31
31
  [[package]]
32
32
  name = "bindgen"
33
- version = "0.60.1"
33
+ version = "0.62.0"
34
34
  source = "registry+https://github.com/rust-lang/crates.io-index"
35
- checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
35
+ checksum = "c6720a8b7b2d39dd533285ed438d458f65b31b5c257e6ac7bb3d7e82844dd722"
36
36
  dependencies = [
37
37
  "bitflags",
38
38
  "cexpr",
@@ -45,6 +45,7 @@ dependencies = [
45
45
  "regex",
46
46
  "rustc-hash",
47
47
  "shlex",
48
+ "syn 1.0.109",
48
49
  ]
49
50
 
50
51
  [[package]]
@@ -169,6 +170,9 @@ dependencies = [
169
170
  "nix",
170
171
  "rand",
171
172
  "rb-sys",
173
+ "rb-sys-env",
174
+ "serde",
175
+ "serde_json",
172
176
  "sha2",
173
177
  "smallvec",
174
178
  "tempfile",
@@ -252,6 +256,12 @@ dependencies = [
252
256
  "windows-sys 0.48.0",
253
257
  ]
254
258
 
259
+ [[package]]
260
+ name = "itoa"
261
+ version = "1.0.6"
262
+ source = "registry+https://github.com/rust-lang/crates.io-index"
263
+ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
264
+
255
265
  [[package]]
256
266
  name = "lazy_static"
257
267
  version = "1.4.0"
@@ -289,7 +299,7 @@ checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
289
299
  [[package]]
290
300
  name = "magnus"
291
301
  version = "0.5.0"
292
- source = "git+https://github.com/matsadler/magnus?branch=main#b10aab48119eb87a872bf0bb4480b1fcebe5d1b9"
302
+ source = "git+https://github.com/matsadler/magnus?branch=main#50cab2380103c39c14765a10bdc4b38a74082285"
293
303
  dependencies = [
294
304
  "magnus-macros",
295
305
  "rb-sys",
@@ -300,7 +310,7 @@ dependencies = [
300
310
  [[package]]
301
311
  name = "magnus-macros"
302
312
  version = "0.4.0"
303
- source = "git+https://github.com/matsadler/magnus?branch=main#b10aab48119eb87a872bf0bb4480b1fcebe5d1b9"
313
+ source = "git+https://github.com/matsadler/magnus?branch=main#50cab2380103c39c14765a10bdc4b38a74082285"
304
314
  dependencies = [
305
315
  "proc-macro2",
306
316
  "quote",
@@ -435,18 +445,18 @@ dependencies = [
435
445
 
436
446
  [[package]]
437
447
  name = "rb-sys"
438
- version = "0.9.71"
448
+ version = "0.9.79"
439
449
  source = "registry+https://github.com/rust-lang/crates.io-index"
440
- checksum = "156bfedced1e236600bcaad538477097ff2ed5c6b474e411d15b791e1d24c0f1"
450
+ checksum = "939fb78db3e4f26665c1d4c7b91ca66d3578335a19aba552d4a6445811d07072"
441
451
  dependencies = [
442
452
  "rb-sys-build",
443
453
  ]
444
454
 
445
455
  [[package]]
446
456
  name = "rb-sys-build"
447
- version = "0.9.71"
457
+ version = "0.9.79"
448
458
  source = "registry+https://github.com/rust-lang/crates.io-index"
449
- checksum = "5cb2e4a32cbc290b543a74567072ad24b708aff7bb5dde5a68d5690379cd7938"
459
+ checksum = "335a95eb0420d52fa94ef12019df3c2c250c6b19cbb3c60bd05cb7e9c362072c"
450
460
  dependencies = [
451
461
  "bindgen",
452
462
  "lazy_static",
@@ -515,6 +525,12 @@ dependencies = [
515
525
  "windows-sys 0.48.0",
516
526
  ]
517
527
 
528
+ [[package]]
529
+ name = "ryu"
530
+ version = "1.0.13"
531
+ source = "registry+https://github.com/rust-lang/crates.io-index"
532
+ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
533
+
518
534
  [[package]]
519
535
  name = "seq-macro"
520
536
  version = "0.3.3"
@@ -526,6 +542,31 @@ name = "serde"
526
542
  version = "1.0.159"
527
543
  source = "registry+https://github.com/rust-lang/crates.io-index"
528
544
  checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
545
+ dependencies = [
546
+ "serde_derive",
547
+ ]
548
+
549
+ [[package]]
550
+ name = "serde_derive"
551
+ version = "1.0.159"
552
+ source = "registry+https://github.com/rust-lang/crates.io-index"
553
+ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
554
+ dependencies = [
555
+ "proc-macro2",
556
+ "quote",
557
+ "syn 2.0.13",
558
+ ]
559
+
560
+ [[package]]
561
+ name = "serde_json"
562
+ version = "1.0.96"
563
+ source = "registry+https://github.com/rust-lang/crates.io-index"
564
+ checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
565
+ dependencies = [
566
+ "itoa",
567
+ "ryu",
568
+ "serde",
569
+ ]
529
570
 
530
571
  [[package]]
531
572
  name = "sha2"
@@ -555,6 +596,9 @@ name = "smallvec"
555
596
  version = "1.10.0"
556
597
  source = "registry+https://github.com/rust-lang/crates.io-index"
557
598
  checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
599
+ dependencies = [
600
+ "serde",
601
+ ]
558
602
 
559
603
  [[package]]
560
604
  name = "syn"
@@ -13,7 +13,9 @@ memmap2 = "0.5"
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
15
  rb-sys = "0.9"
16
- smallvec = "1.10"
16
+ serde = { version = "1.0", features = ["derive"] }
17
+ serde_json = { version = "1.0", features = ["raw_value"] }
18
+ smallvec = { version = "1.10", features = ["serde"] }
17
19
  thiserror = "1.0"
18
20
 
19
21
  [dev-dependencies]
@@ -25,6 +27,9 @@ rand = "0.8"
25
27
  sha2 = "0.10"
26
28
  tempfile = "3.5"
27
29
 
30
+ [build-dependencies]
31
+ rb-sys-env = "0.1"
32
+
28
33
  [lib]
29
34
  # Integration tests won't work if crate is only `cdylib`.
30
35
  crate-type = ["cdylib","lib"]
@@ -0,0 +1,5 @@
1
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
2
+ let _ = rb_sys_env::activate()?;
3
+
4
+ Ok(())
5
+ }
@@ -1,10 +1,12 @@
1
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)]
@@ -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() {
@@ -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 {
@@ -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
 
@@ -418,10 +418,10 @@ impl MmapedFile {
418
418
 
419
419
  raw_str.as_mut().as_.heap.ptr = self.as_mut_ptr().offset(offset);
420
420
 
421
- let current_len = raw_str.as_ref().as_.heap.len;
421
+ let current_len = str.len() as c_long;
422
422
  let new_shared_len = old_cap + current_len;
423
423
 
424
- raw_str.as_mut().as_.heap.len = new_shared_len;
424
+ self.update_rstring_len(raw_str, new_shared_len);
425
425
  continue;
426
426
  }
427
427
 
@@ -436,7 +436,7 @@ impl MmapedFile {
436
436
  //
437
437
  // See https://gitlab.com/gitlab-org/ruby/gems/prometheus-client-mmap/-/issues/45
438
438
  raw_str.as_mut().as_.heap.ptr = self.as_mut_ptr();
439
- raw_str.as_mut().as_.heap.len = new_len;
439
+ self.update_rstring_len(raw_str, new_len);
440
440
  }
441
441
  }
442
442
 
@@ -632,6 +632,16 @@ impl MmapedFile {
632
632
  unsafe fn rb_string_internal(rb_str: RString) -> NonNull<rb_sys::RString> {
633
633
  mem::transmute::<RString, NonNull<rb_sys::RString>>(rb_str)
634
634
  }
635
+
636
+ #[cfg(ruby_lte_3_2)]
637
+ unsafe fn update_rstring_len(&self, mut raw_str: NonNull<rb_sys::RString>, new_len: c_long) {
638
+ raw_str.as_mut().as_.heap.len = new_len;
639
+ }
640
+
641
+ #[cfg(ruby_gte_3_3)]
642
+ unsafe fn update_rstring_len(&self, mut raw_str: NonNull<rb_sys::RString>, new_len: c_long) {
643
+ raw_str.as_mut().len = new_len;
644
+ }
635
645
  }
636
646
 
637
647
  #[cfg(test)]
@@ -772,8 +782,7 @@ mod test {
772
782
  if str.as_raw() == child_id {
773
783
  assert_eq!(parent_id, raw_str.as_ref().as_.heap.aux.shared);
774
784
 
775
- let child_offset =
776
- mmap_len as isize - raw_str.as_ref().as_.heap.len as isize;
785
+ let child_offset = mmap_len as isize - str.len() as isize;
777
786
  assert_eq!(mmap_ptr.offset(child_offset), raw_str.as_ref().as_.heap.ptr);
778
787
 
779
788
  child_checked = true;
@@ -781,7 +790,7 @@ mod test {
781
790
  assert_eq!(parent_id, str.as_raw());
782
791
 
783
792
  assert_eq!(mmap_ptr, raw_str.as_ref().as_.heap.ptr);
784
- assert_eq!(mmap_len as c_long, raw_str.as_ref().as_.heap.len);
793
+ assert_eq!(mmap_len as c_long, str.len() as c_long);
785
794
  assert!(raw_str.as_ref().basic.flags & (STR_SHARED | STR_NOEMBED) > 0);
786
795
  assert!(str.is_frozen());
787
796
 
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.24.4'.freeze
3
+ VERSION = '0.25.0'.freeze
4
4
  end
5
5
  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.24.4
4
+ version: 0.25.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Tobias Schmidt
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2023-06-12 00:00:00.000000000 Z
14
+ date: 2023-06-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rb_sys
@@ -150,6 +150,7 @@ files:
150
150
  - ext/fast_mmaped_file_rs/Cargo.lock
151
151
  - ext/fast_mmaped_file_rs/Cargo.toml
152
152
  - ext/fast_mmaped_file_rs/README.md
153
+ - ext/fast_mmaped_file_rs/build.rs
153
154
  - ext/fast_mmaped_file_rs/extconf.rb
154
155
  - ext/fast_mmaped_file_rs/src/error.rs
155
156
  - ext/fast_mmaped_file_rs/src/file_entry.rs
@@ -159,7 +160,6 @@ files:
159
160
  - ext/fast_mmaped_file_rs/src/map.rs
160
161
  - ext/fast_mmaped_file_rs/src/mmap.rs
161
162
  - ext/fast_mmaped_file_rs/src/mmap/inner.rs
162
- - ext/fast_mmaped_file_rs/src/parser.rs
163
163
  - ext/fast_mmaped_file_rs/src/raw_entry.rs
164
164
  - ext/fast_mmaped_file_rs/src/testhelper.rs
165
165
  - ext/fast_mmaped_file_rs/src/util.rs
@@ -1,448 +0,0 @@
1
- use smallvec::SmallVec;
2
- use std::str;
3
-
4
- /// String slices pointing to the fields of a borrowed `Entry`'s JSON data.
5
- #[derive(PartialEq, Eq, Debug)]
6
- pub struct MetricText<'a> {
7
- pub family_name: &'a str,
8
- pub metric_name: &'a str,
9
- pub labels: SmallVec<[&'a str; 4]>,
10
- pub values: SmallVec<[&'a str; 4]>,
11
- }
12
-
13
- #[derive(PartialEq, Eq, Debug)]
14
- struct MetricNames<'a> {
15
- label_json: &'a str,
16
- family_name: &'a str,
17
- metric_name: &'a str,
18
- }
19
-
20
- #[derive(PartialEq, Eq, Debug)]
21
- struct MetricLabelVals<'a> {
22
- labels: SmallVec<[&'a str; 4]>,
23
- values: SmallVec<[&'a str; 4]>,
24
- }
25
-
26
- /// Parse Prometheus metric data stored in the following format:
27
- ///
28
- /// ["metric","name",["label_a","label_b"],["value_a","value_b"]]
29
- ///
30
- /// There will be 1-8 trailing spaces to ensure at least a one-byte
31
- /// gap between the json and value, and to pad to an 8-byte alignment.
32
- /// We strip the surrounding double quotes from all items. Values may
33
- /// or may not have surrounding double quotes, depending on their type.
34
- pub fn parse_metrics(json: &str) -> Option<MetricText> {
35
- // It would be preferable to use `serde_json` here, but the values
36
- // may be strings, numbers, or null, and so don't parse easily to a
37
- // defined struct. Using `serde_json::Value` is an option, but since
38
- // we're just copying the literal values into a buffer this will be
39
- // inefficient and verbose.
40
-
41
- // Trim trailing spaces from string before processing. We use
42
- // `trim_end_matches()` instead of `trim_end()` because we know the
43
- // trailing bytes are always ASCII 0x20 bytes. `trim_end()` will also
44
- // check for unicode spaces and consume a few more CPU cycles.
45
- let trimmed = json.trim_end_matches(' ');
46
-
47
- let names = parse_names(trimmed)?;
48
- let label_json = names.label_json;
49
-
50
- let label_vals = parse_label_values(label_json)?;
51
-
52
- Some(MetricText {
53
- family_name: names.family_name,
54
- metric_name: names.metric_name,
55
- labels: label_vals.labels,
56
- values: label_vals.values,
57
- })
58
- }
59
-
60
- fn parse_names(json: &str) -> Option<MetricNames> {
61
- // Starting with: ["family_name","metric_name",[...
62
- if !json.starts_with("[\"") {
63
- return None;
64
- }
65
-
66
- // Captured: "family_name","metric_name",
67
- // ^^^^^^^^^^^
68
- let (family_name, remainder) = scan_str(json.get(1..)?)?;
69
-
70
- // Validate comma separated names
71
- // ["family_name","metric_name",[...
72
- // ^
73
- if !remainder.starts_with("\",") {
74
- return None;
75
- }
76
-
77
- // Now: "metric_name",[...
78
- let remainder = remainder.get(2..)?;
79
-
80
- // Captured: "family_name","metric_name",
81
- // ^^^^^^^^^^^
82
- let (metric_name, remainder) = scan_str(remainder)?;
83
-
84
- // Validate comma separated names
85
- // ["family_name","metric_name",[...
86
- // ^^
87
- if !remainder.starts_with("\",") {
88
- return None;
89
- }
90
-
91
- // Save the rest of the slice to parse for labels later.
92
- // Now: [...
93
- let label_json = remainder.get(2..)?;
94
-
95
- Some(MetricNames {
96
- label_json,
97
- family_name,
98
- metric_name,
99
- })
100
- }
101
-
102
- fn parse_label_values(json: &str) -> Option<MetricLabelVals> {
103
- // Starting with: ["label_a","label_b"],["value_a", "value_b"]]
104
- if !json.starts_with('[') {
105
- return None;
106
- }
107
-
108
- // Now: "label_a","label_b"],["value_a", "value_b"]]
109
- let mut remainder = json.get(1..)?;
110
-
111
- // Validate we either have the start of a label string or an
112
- // empty array, e.g. `["` or `[]`.
113
- if !matches!(json.as_bytes().get(1)?, b'"' | b']') {
114
- return None;
115
- }
116
-
117
- let mut labels = SmallVec::new();
118
-
119
- // Split on commas into:
120
- // "label_a","label_b"
121
- // ^^^one^^^ ^^^two^^^
122
- loop {
123
- let Some((label, label_rem)) = scan_str(remainder) else {
124
- // No further keys.
125
- break;
126
- };
127
- labels.push(label);
128
-
129
- // Advance past trailing quote.
130
- remainder = label_rem.get(1..)?;
131
- match remainder.as_bytes().first() {
132
- Some(b']') => break, // No further labels.
133
- Some(b',') => {} // More labels expected.
134
- _ => return None, // Invalid.
135
- };
136
-
137
- // Advance past comma.
138
- remainder = remainder.get(1..)?;
139
- }
140
-
141
- if !remainder.starts_with("],[") {
142
- return None;
143
- }
144
- // Now: "value_a", "value_b"]]
145
- remainder = remainder.get(3..)?;
146
-
147
- // Validate we don't have extra brackets.
148
- if remainder.starts_with('[') {
149
- return None;
150
- }
151
-
152
- let mut values = SmallVec::new();
153
- // Split on commas into:
154
- // "value_a","value_b"
155
- // ^^^one^^^ ^^^two^^^
156
- loop {
157
- if remainder.starts_with('"') {
158
- let (value, value_rem) = scan_str(remainder)?;
159
- values.push(value);
160
-
161
- // Advance past trailing quote.
162
- remainder = value_rem.get(1..)?;
163
- } else {
164
- // An unquoted value, such as `true` or `404`.
165
- let i = remainder.find(|c| c == ',' || c == ']')?;
166
- let value = &remainder[..i];
167
-
168
- match (value.is_empty(), is_valid_json_literal(value)) {
169
- (true, _) => {} // Empty string, do nothing.
170
- (false, true) => values.push(value), // Valid literal.
171
- (false, false) => return None, // Invalid literal, fail.
172
- }
173
-
174
- remainder = &remainder[i..];
175
- }
176
-
177
- match remainder.as_bytes().first() {
178
- Some(b']') => break, // End of values.
179
- Some(b',') => {} // More values expected.
180
- _ => return None, // Invalid.
181
- };
182
-
183
- // Advance past comma.
184
- remainder = remainder.get(1..)?;
185
- }
186
-
187
- if values.len() != labels.len() {
188
- return None;
189
- }
190
-
191
- // Now: ]]
192
- if remainder != "]]" {
193
- return None;
194
- }
195
-
196
- Some(MetricLabelVals { labels, values })
197
- }
198
-
199
- // Read a JSON-encoded str that includes starting and ending double quotes.
200
- // Returns the validated str with the double quotes trimmed and the remainder
201
- // of the input str.
202
- fn scan_str(json: &str) -> Option<(&str, &str)> {
203
- let mut escaping = false;
204
-
205
- if !json.starts_with('"') {
206
- return None;
207
- }
208
-
209
- // Trim leading double quote.
210
- let json = json.get(1..)?;
211
-
212
- for (i, &c) in json.as_bytes().iter().enumerate() {
213
- if c == b'\\' {
214
- escaping = true;
215
- continue;
216
- }
217
-
218
- if c == b'"' && !escaping {
219
- return Some((json.get(..i)?, json.get(i..)?));
220
- }
221
-
222
- escaping = false;
223
- }
224
-
225
- None
226
- }
227
-
228
- // Confirm an unquoted JSON literal is a boolean, null, or has a passing
229
- // resemblance to a number. We do not confirm numeric formatting, only
230
- // that all characters are valid. See https://www.json.org/json-en.html
231
- // for details.
232
- fn is_valid_json_literal(s: &str) -> bool {
233
- match s {
234
- "true" | "false" | "null" => true,
235
- _ => s.chars().all(|c| {
236
- c.is_ascii_digit() || c == '.' || c == '+' || c == '-' || c == 'e' || c == 'E'
237
- }),
238
- }
239
- }
240
-
241
- #[cfg(test)]
242
- mod test {
243
- use smallvec::smallvec;
244
-
245
- use super::*;
246
-
247
- struct TestCase {
248
- name: &'static str,
249
- input: &'static str,
250
- expected: Option<MetricText<'static>>,
251
- }
252
-
253
- #[test]
254
- fn valid_json() {
255
- let tc = vec![
256
- TestCase {
257
- name: "basic",
258
- input: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
259
- expected: Some(MetricText {
260
- family_name: "metric",
261
- metric_name: "name",
262
- labels: smallvec!["label_a", "label_b"],
263
- values: smallvec!["value_a", "value_b"],
264
- }),
265
- },
266
- TestCase {
267
- name: "many labels",
268
- input: r#"["metric","name",["label_a","label_b","label_c","label_d","label_e"],["value_a","value_b","value_c","value_d","value_e"]]"#,
269
-
270
- expected: Some(MetricText {
271
- family_name: "metric",
272
- metric_name: "name",
273
- labels: smallvec!["label_a", "label_b", "label_c", "label_d", "label_e"],
274
- values: smallvec!["value_a", "value_b", "value_c", "value_d", "value_e"],
275
- }),
276
- },
277
- TestCase {
278
- name: "numeric value",
279
- input: r#"["metric","name",["label_a","label_b"],["value_a",403]]"#,
280
- expected: Some(MetricText {
281
- family_name: "metric",
282
- metric_name: "name",
283
- labels: smallvec!["label_a", "label_b"],
284
- values: smallvec!["value_a", "403"],
285
- }),
286
- },
287
- TestCase {
288
- name: "scientific notation literal",
289
- input: r#"["metric","name",["label_a"],[-2.0e-5]]"#,
290
- expected: Some(MetricText {
291
- family_name: "metric",
292
- metric_name: "name",
293
- labels: smallvec!["label_a"],
294
- values: smallvec!["-2.0e-5"],
295
- }),
296
- },
297
- TestCase {
298
- name: "null value",
299
- input: r#"["metric","name",["label_a","label_b"],[null,"value_b"]]"#,
300
- expected: Some(MetricText {
301
- family_name: "metric",
302
- metric_name: "name",
303
- labels: smallvec!["label_a", "label_b"],
304
- values: smallvec!["null", "value_b"],
305
- }),
306
- },
307
- TestCase {
308
- name: "no labels",
309
- input: r#"["metric","name",[],[]]"#,
310
- expected: Some(MetricText {
311
- family_name: "metric",
312
- metric_name: "name",
313
- labels: smallvec![],
314
- values: smallvec![],
315
- }),
316
- },
317
- TestCase {
318
- name: "comma in items",
319
- input: r#"["met,ric","na,me",["label,_a","label_b"],["value,_a","value_b"]]"#,
320
- expected: Some(MetricText {
321
- family_name: "met,ric",
322
- metric_name: "na,me",
323
- labels: smallvec!["label,_a", "label_b"],
324
- values: smallvec!["value,_a", "value_b"],
325
- }),
326
- },
327
- TestCase {
328
- name: "bracket in value",
329
- input: r#"["met[r]ic","na[m]e",["label[_]a","label_b"],["value_a","val[ue]_b"]]"#,
330
- expected: Some(MetricText {
331
- family_name: "met[r]ic",
332
- metric_name: "na[m]e",
333
- labels: smallvec!["label[_]a", "label_b"],
334
- values: smallvec!["value_a", "val[ue]_b"],
335
- }),
336
- },
337
- TestCase {
338
- name: "escaped quotes",
339
- input: r#"["met\"ric","na\"me",["label\"_a","label_b"],["value\"_a","value_b"]]"#,
340
- expected: Some(MetricText {
341
- family_name: r#"met\"ric"#,
342
- metric_name: r#"na\"me"#,
343
- labels: smallvec![r#"label\"_a"#, "label_b"],
344
- values: smallvec![r#"value\"_a"#, "value_b"],
345
- }),
346
- },
347
- ];
348
-
349
- for case in tc {
350
- assert_eq!(
351
- parse_metrics(case.input),
352
- case.expected,
353
- "test case: {}",
354
- case.name,
355
- );
356
- }
357
- }
358
-
359
- #[test]
360
- fn invalid_json() {
361
- let tc = vec![
362
- TestCase {
363
- name: "not json",
364
- input: "hello, world",
365
- expected: None,
366
- },
367
- TestCase {
368
- name: "no names",
369
- input: r#"[["label_a","label_b"],["value_a","value_b"]]"#,
370
- expected: None,
371
- },
372
- TestCase {
373
- name: "too many names",
374
- input: r#"["metric","name","unexpected_name",["label_a","label_b"],["value_a","value_b"]]"#,
375
- expected: None,
376
- },
377
- TestCase {
378
- name: "too many labels",
379
- input: r#"["metric","name","unexpected_name",["label_a","label_b","label_c"],["value_a","value_b"]]"#,
380
- expected: None,
381
- },
382
- TestCase {
383
- name: "too many values",
384
- input: r#"["metric","name",["label_a","label_b"],["value_a","value_b",null]]"#,
385
- expected: None,
386
- },
387
- TestCase {
388
- name: "no values",
389
- input: r#"["metric","name",["label_a","label_b"]"#,
390
- expected: None,
391
- },
392
- TestCase {
393
- name: "no arrays",
394
- input: r#"["metric","name","label_a","value_a"]"#,
395
- expected: None,
396
- },
397
- TestCase {
398
- name: "too many leading brackets",
399
- input: r#"[["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
400
- expected: None,
401
- },
402
- TestCase {
403
- name: "too many trailing brackets",
404
- input: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]]"#,
405
- expected: None,
406
- },
407
- TestCase {
408
- name: "too many leading label brackets",
409
- input: r#"["metric","name",[["label_a","label_b"],["value_a","value_b"]]"#,
410
- expected: None,
411
- },
412
- TestCase {
413
- name: "too many trailing label brackets",
414
- input: r#"["metric","name",["label_a","label_b"]],["value_a","value_b"]]"#,
415
- expected: None,
416
- },
417
- TestCase {
418
- name: "too many leading value brackets",
419
- input: r#"["metric","name",["label_a","label_b"],[["value_a","value_b"]]"#,
420
- expected: None,
421
- },
422
- TestCase {
423
- name: "misplaced bracket",
424
- input: r#"["metric","name",["label_a","label_b"],]["value_a","value_b"]]"#,
425
- expected: None,
426
- },
427
- TestCase {
428
- name: "comma in numeric value",
429
- input: r#"["metric","name",["label_a","label_b"],[400,0,"value_b"]]"#,
430
- expected: None,
431
- },
432
- TestCase {
433
- name: "non-e letter in numeric value",
434
- input: r#"["metric","name",["label_a","label_b"],[400x0,"value_b"]]"#,
435
- expected: None,
436
- },
437
- ];
438
-
439
- for case in tc {
440
- assert_eq!(
441
- case.expected,
442
- parse_metrics(case.input),
443
- "test case: {}",
444
- case.name,
445
- );
446
- }
447
- }
448
- }