vinted-prometheus-client-mmap 1.5.0-x86_64-linux

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -0
  3. data/ext/fast_mmaped_file_rs/Cargo.toml +40 -0
  4. data/ext/fast_mmaped_file_rs/README.md +52 -0
  5. data/ext/fast_mmaped_file_rs/build.rs +7 -0
  6. data/ext/fast_mmaped_file_rs/extconf.rb +28 -0
  7. data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
  8. data/ext/fast_mmaped_file_rs/src/exemplars.rs +25 -0
  9. data/ext/fast_mmaped_file_rs/src/file_entry.rs +1252 -0
  10. data/ext/fast_mmaped_file_rs/src/file_info.rs +240 -0
  11. data/ext/fast_mmaped_file_rs/src/lib.rs +89 -0
  12. data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
  13. data/ext/fast_mmaped_file_rs/src/map.rs +519 -0
  14. data/ext/fast_mmaped_file_rs/src/metrics.proto +153 -0
  15. data/ext/fast_mmaped_file_rs/src/mmap/inner.rs +775 -0
  16. data/ext/fast_mmaped_file_rs/src/mmap.rs +977 -0
  17. data/ext/fast_mmaped_file_rs/src/raw_entry.rs +547 -0
  18. data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
  19. data/ext/fast_mmaped_file_rs/src/util.rs +140 -0
  20. data/lib/.DS_Store +0 -0
  21. data/lib/2.7/fast_mmaped_file_rs.so +0 -0
  22. data/lib/3.0/fast_mmaped_file_rs.so +0 -0
  23. data/lib/3.1/fast_mmaped_file_rs.so +0 -0
  24. data/lib/3.2/fast_mmaped_file_rs.so +0 -0
  25. data/lib/3.3/fast_mmaped_file_rs.so +0 -0
  26. data/lib/prometheus/.DS_Store +0 -0
  27. data/lib/prometheus/client/configuration.rb +24 -0
  28. data/lib/prometheus/client/counter.rb +27 -0
  29. data/lib/prometheus/client/formats/protobuf.rb +93 -0
  30. data/lib/prometheus/client/formats/text.rb +85 -0
  31. data/lib/prometheus/client/gauge.rb +40 -0
  32. data/lib/prometheus/client/helper/entry_parser.rb +132 -0
  33. data/lib/prometheus/client/helper/file_locker.rb +50 -0
  34. data/lib/prometheus/client/helper/json_parser.rb +23 -0
  35. data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
  36. data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
  37. data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
  38. data/lib/prometheus/client/helper/plain_file.rb +29 -0
  39. data/lib/prometheus/client/histogram.rb +80 -0
  40. data/lib/prometheus/client/label_set_validator.rb +85 -0
  41. data/lib/prometheus/client/metric.rb +80 -0
  42. data/lib/prometheus/client/mmaped_dict.rb +83 -0
  43. data/lib/prometheus/client/mmaped_value.rb +164 -0
  44. data/lib/prometheus/client/page_size.rb +17 -0
  45. data/lib/prometheus/client/push.rb +203 -0
  46. data/lib/prometheus/client/rack/collector.rb +88 -0
  47. data/lib/prometheus/client/rack/exporter.rb +102 -0
  48. data/lib/prometheus/client/registry.rb +65 -0
  49. data/lib/prometheus/client/simple_value.rb +31 -0
  50. data/lib/prometheus/client/summary.rb +69 -0
  51. data/lib/prometheus/client/support/puma.rb +44 -0
  52. data/lib/prometheus/client/support/unicorn.rb +35 -0
  53. data/lib/prometheus/client/uses_value_type.rb +20 -0
  54. data/lib/prometheus/client/version.rb +5 -0
  55. data/lib/prometheus/client.rb +58 -0
  56. data/lib/prometheus.rb +3 -0
  57. metadata +210 -0
@@ -0,0 +1,547 @@
1
+ use std::mem::size_of;
2
+
3
+ use crate::error::MmapError;
4
+ use crate::exemplars::{Exemplar, EXEMPLAR_ENTRY_MAX_SIZE_BYTES};
5
+ use crate::util;
6
+ use crate::util::CheckedOps;
7
+ use crate::Result;
8
+
9
+ /// The logic to save a `MetricsEntry`, or parse one from a byte slice.
10
+ #[derive(PartialEq, Eq, Clone, Debug)]
11
+ pub struct RawEntry<'a> {
12
+ bytes: &'a [u8],
13
+ encoded_len: usize,
14
+ }
15
+
16
+ impl<'a> RawEntry<'a> {
17
+ pub fn save_exemplar(bytes: &'a mut [u8], key: &[u8], value: Exemplar) -> Result<usize> {
18
+ let total_len = Self::calc_total_len_exemplar(key.len())?;
19
+
20
+ if total_len > bytes.len() {
21
+ return Err(MmapError::Other(format!(
22
+ "entry length {total_len} larger than slice length {}",
23
+ bytes.len()
24
+ )));
25
+ }
26
+
27
+ let val = serde_json::to_string(&value).unwrap();
28
+
29
+ // CAST: `calc_len` runs `check_encoded_len`, we know the key len
30
+ // is less than i32::MAX. No risk of overflows or failed casts.
31
+ let key_len: u32 = key.len() as u32;
32
+
33
+ // Write the key length to the mmap.
34
+ bytes[..size_of::<u32>()].copy_from_slice(&key_len.to_ne_bytes());
35
+
36
+ // Advance slice past the size.
37
+ let bytes = &mut bytes[size_of::<u32>()..];
38
+
39
+ bytes[..key.len()].copy_from_slice(key);
40
+
41
+ // Advance to end of key.
42
+ let bytes = &mut bytes[key.len()..];
43
+
44
+ let pad_len = Self::padding_len(key.len());
45
+ bytes[..pad_len].fill(b' ');
46
+ let bytes = &mut bytes[pad_len..];
47
+
48
+ bytes[..val.len()].copy_from_slice(val.as_bytes());
49
+
50
+ Self::calc_value_offset(key.len())
51
+ }
52
+
53
+ /// Save an entry to the mmap, returning the value offset in the newly created entry.
54
+ pub fn save(bytes: &'a mut [u8], key: &[u8], value: f64) -> Result<usize> {
55
+ let total_len = Self::calc_total_len(key.len())?;
56
+
57
+ if total_len > bytes.len() {
58
+ return Err(MmapError::Other(format!(
59
+ "entry length {total_len} larger than slice length {}",
60
+ bytes.len()
61
+ )));
62
+ }
63
+
64
+ // CAST: `calc_len` runs `check_encoded_len`, we know the key len
65
+ // is less than i32::MAX. No risk of overflows or failed casts.
66
+ let key_len: u32 = key.len() as u32;
67
+
68
+ // Write the key length to the mmap.
69
+ bytes[..size_of::<u32>()].copy_from_slice(&key_len.to_ne_bytes());
70
+
71
+ // Advance slice past the size.
72
+ let bytes = &mut bytes[size_of::<u32>()..];
73
+
74
+ bytes[..key.len()].copy_from_slice(key);
75
+
76
+ // Advance to end of key.
77
+ let bytes = &mut bytes[key.len()..];
78
+
79
+ let pad_len = Self::padding_len(key.len());
80
+ bytes[..pad_len].fill(b' ');
81
+ let bytes = &mut bytes[pad_len..];
82
+
83
+ bytes[..size_of::<f64>()].copy_from_slice(&value.to_ne_bytes());
84
+
85
+ Self::calc_value_offset(key.len())
86
+ }
87
+
88
+ /// Parse a byte slice starting into an `MmapEntry`.
89
+ pub fn from_slice(bytes: &'a [u8]) -> Result<Self> {
90
+ // CAST: no-op on 32-bit, widening on 64-bit.
91
+ let encoded_len = util::read_u32(bytes, 0)? as usize;
92
+
93
+ let total_len = Self::calc_total_len(encoded_len)?;
94
+
95
+ // Confirm the value is in bounds of the slice provided.
96
+ if total_len > bytes.len() {
97
+ return Err(MmapError::out_of_bounds(total_len, bytes.len()));
98
+ }
99
+
100
+ // Advance slice past length int and cut at end of entry.
101
+ let bytes = &bytes[size_of::<u32>()..total_len];
102
+
103
+ Ok(Self { bytes, encoded_len })
104
+ }
105
+
106
+ pub fn from_slice_exemplar(bytes: &'a [u8]) -> Result<Self> {
107
+ // CAST: no-op on 32-bit, widening on 64-bit.
108
+ let encoded_len = util::read_u32(bytes, 0)? as usize;
109
+
110
+ let total_len = Self::calc_total_len_exemplar(encoded_len)?;
111
+
112
+ // Confirm the value is in bounds of the slice provided.
113
+ if total_len > bytes.len() {
114
+ return Err(MmapError::out_of_bounds(total_len, bytes.len()));
115
+ }
116
+
117
+ // Advance slice past length int and cut at end of entry.
118
+ let bytes = &bytes[size_of::<u32>()..total_len];
119
+
120
+ Ok(Self { bytes, encoded_len })
121
+ }
122
+
123
+ /// Read the `f64` value of an entry from memory.
124
+ #[inline]
125
+ pub fn value(&self) -> f64 {
126
+ // We've stripped off the leading u32, don't include that here.
127
+ let offset = self.encoded_len + Self::padding_len(self.encoded_len);
128
+
129
+ // UNWRAP: We confirm in the constructor that the value offset
130
+ // is in-range for the slice.
131
+ util::read_f64(self.bytes, offset).unwrap()
132
+ }
133
+
134
+ pub fn exemplar(&self) -> Exemplar {
135
+ // We've stripped off the leading u32, don't include that here.
136
+ let offset = self.encoded_len + Self::padding_len(self.encoded_len);
137
+
138
+ // UNWRAP: We confirm in the constructor that the value offset
139
+ // is in-range for the slice.
140
+ util::read_exemplar(self.bytes, offset).unwrap()
141
+ }
142
+
143
+ /// The length of the entry key without padding.
144
+ #[inline]
145
+ pub fn encoded_len(&self) -> usize {
146
+ self.encoded_len
147
+ }
148
+
149
+ /// Returns a slice with the JSON string in the entry, excluding padding.
150
+ #[inline]
151
+ pub fn json(&self) -> &[u8] {
152
+ &self.bytes[..self.encoded_len]
153
+ }
154
+
155
+ /// Calculate the total length of an `MmapEntry`, including the string length,
156
+ /// string, padding, and value.
157
+ #[inline]
158
+ pub fn total_len(&self) -> usize {
159
+ // UNWRAP:: We confirmed in the constructor that this doesn't overflow.
160
+ Self::calc_total_len(self.encoded_len).unwrap()
161
+ }
162
+
163
+ #[inline]
164
+ pub fn total_len_exemplar(&self) -> usize {
165
+ // UNWRAP:: We confirmed in the constructor that this doesn't overflow.
166
+ Self::calc_total_len_exemplar(self.encoded_len).unwrap()
167
+ }
168
+
169
+ /// Calculate the total length of an `MmapEntry`, including the string length,
170
+ /// string, padding, and value. Validates encoding_len is within expected bounds.
171
+ #[inline]
172
+ pub fn calc_total_len(encoded_len: usize) -> Result<usize> {
173
+ Self::calc_value_offset(encoded_len)?.add_chk(size_of::<f64>())
174
+ }
175
+
176
+ #[inline]
177
+ pub fn calc_total_len_exemplar(encoded_len: usize) -> Result<usize> {
178
+ Self::calc_value_offset(encoded_len)?.add_chk(EXEMPLAR_ENTRY_MAX_SIZE_BYTES)
179
+ }
180
+
181
+ /// Calculate the value offset of an `MmapEntry`, including the string length,
182
+ /// string, padding. Validates encoding_len is within expected bounds.
183
+ #[inline]
184
+ pub fn calc_value_offset(encoded_len: usize) -> Result<usize> {
185
+ Self::check_encoded_len(encoded_len)?;
186
+
187
+ Ok(size_of::<u32>() + encoded_len + Self::padding_len(encoded_len))
188
+ }
189
+
190
+ /// Calculate the number of padding bytes to add to the value key to reach
191
+ /// 8-byte alignment. Does not validate key length.
192
+ #[inline]
193
+ pub fn padding_len(encoded_len: usize) -> usize {
194
+ 8 - (size_of::<u32>() + encoded_len) % 8
195
+ }
196
+
197
+ #[inline]
198
+ fn check_encoded_len(encoded_len: usize) -> Result<()> {
199
+ if encoded_len as u64 > i32::MAX as u64 {
200
+ return Err(MmapError::KeyLength);
201
+ }
202
+ Ok(())
203
+ }
204
+ }
205
+
206
+ #[cfg(test)]
207
+ mod test {
208
+ use bstr::ByteSlice;
209
+
210
+ use super::*;
211
+ use crate::testhelper::TestEntry;
212
+
213
+ #[test]
214
+ fn test_from_slice() {
215
+ #[derive(PartialEq, Default, Debug)]
216
+ struct TestCase {
217
+ name: &'static str,
218
+ input: TestEntry,
219
+ expected_enc_len: Option<usize>,
220
+ expected_err: Option<MmapError>,
221
+ }
222
+
223
+ let tc = vec![
224
+ TestCase {
225
+ name: "ok",
226
+ input: TestEntry {
227
+ header: 61,
228
+ json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
229
+ padding_len: 7,
230
+ value: 1.0,
231
+ },
232
+ expected_enc_len: Some(61),
233
+ ..Default::default()
234
+ },
235
+ TestCase {
236
+ name: "zero length key",
237
+ input: TestEntry {
238
+ header: 0,
239
+ json: "",
240
+ padding_len: 4,
241
+ value: 1.0,
242
+ },
243
+ expected_enc_len: Some(0),
244
+ ..Default::default()
245
+ },
246
+ TestCase {
247
+ name: "header value too large",
248
+ input: TestEntry {
249
+ header: i32::MAX as u32 + 1,
250
+ json: "foo",
251
+ padding_len: 1,
252
+ value: 0.0,
253
+ },
254
+ expected_err: Some(MmapError::KeyLength),
255
+ ..Default::default()
256
+ },
257
+ TestCase {
258
+ name: "header value much longer than json len",
259
+ input: TestEntry {
260
+ header: 256,
261
+ json: "foobar",
262
+ padding_len: 6,
263
+ value: 1.0,
264
+ },
265
+ expected_err: Some(MmapError::out_of_bounds(272, 24)),
266
+ ..Default::default()
267
+ },
268
+ TestCase {
269
+ // Situations where encoded_len is wrong but padding makes the
270
+ // value offset the same are not caught.
271
+ name: "header off by one",
272
+ input: TestEntry {
273
+ header: 4,
274
+ json: "123",
275
+ padding_len: 1,
276
+ value: 1.0,
277
+ },
278
+ expected_err: Some(MmapError::out_of_bounds(24, 16)),
279
+ ..Default::default()
280
+ },
281
+ ];
282
+
283
+ for case in tc {
284
+ let name = case.name;
285
+ let input = case.input.as_bstring();
286
+
287
+ let resp = RawEntry::from_slice(&input);
288
+
289
+ if case.expected_err.is_none() {
290
+ let expected_buf = case.input.as_bytes_no_header();
291
+ let resp = resp.as_ref().unwrap();
292
+ let bytes = resp.bytes;
293
+
294
+ assert_eq!(expected_buf, bytes.as_bstr(), "test case: {name} - bytes",);
295
+
296
+ assert_eq!(
297
+ resp.json(),
298
+ case.input.json.as_bytes(),
299
+ "test case: {name} - json matches"
300
+ );
301
+
302
+ assert_eq!(
303
+ resp.total_len(),
304
+ case.input.as_bstring().len(),
305
+ "test case: {name} - total_len matches"
306
+ );
307
+
308
+ assert_eq!(
309
+ resp.encoded_len(),
310
+ case.input.json.len(),
311
+ "test case: {name} - encoded_len matches"
312
+ );
313
+
314
+ assert!(
315
+ resp.json().iter().all(|&c| c != b' '),
316
+ "test case: {name} - no spaces in json"
317
+ );
318
+
319
+ let padding_len = RawEntry::padding_len(case.input.json.len());
320
+ assert!(
321
+ bytes[resp.encoded_len..resp.encoded_len + padding_len]
322
+ .iter()
323
+ .all(|&c| c == b' '),
324
+ "test case: {name} - padding is spaces"
325
+ );
326
+
327
+ assert_eq!(
328
+ resp.value(),
329
+ case.input.value,
330
+ "test case: {name} - value is correct"
331
+ );
332
+ }
333
+
334
+ if let Some(expected_enc_len) = case.expected_enc_len {
335
+ assert_eq!(
336
+ expected_enc_len,
337
+ resp.as_ref().unwrap().encoded_len,
338
+ "test case: {name} - encoded len",
339
+ );
340
+ }
341
+
342
+ if let Some(expected_err) = case.expected_err {
343
+ assert_eq!(expected_err, resp.unwrap_err(), "test case: {name} - error",);
344
+ }
345
+ }
346
+ }
347
+
348
+ #[test]
349
+ fn test_save() {
350
+ struct TestCase {
351
+ name: &'static str,
352
+ key: &'static [u8],
353
+ value: f64,
354
+ buf_len: usize,
355
+ expected_entry: Option<TestEntry>,
356
+ expected_resp: Result<usize>,
357
+ }
358
+
359
+ // TODO No test case to validate keys with len > i32::MAX, adding a static that large crashes
360
+ // the test binary.
361
+ let tc = vec![
362
+ TestCase {
363
+ name: "ok",
364
+ key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
365
+ value: 256.0,
366
+ buf_len: 256,
367
+ expected_entry: Some(TestEntry {
368
+ header: 61,
369
+ json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
370
+ padding_len: 7,
371
+ value: 256.0,
372
+ }),
373
+ expected_resp: Ok(72),
374
+ },
375
+ TestCase {
376
+ name: "zero length key",
377
+ key: b"",
378
+ value: 1.0,
379
+ buf_len: 256,
380
+ expected_entry: Some(TestEntry {
381
+ header: 0,
382
+ json: "",
383
+ padding_len: 4,
384
+ value: 1.0,
385
+ }),
386
+ expected_resp: Ok(8),
387
+ },
388
+ TestCase {
389
+ name: "infinite value",
390
+ key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
391
+ value: f64::INFINITY,
392
+ buf_len: 256,
393
+ expected_entry: Some(TestEntry {
394
+ header: 61,
395
+ json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
396
+ padding_len: 7,
397
+ value: f64::INFINITY,
398
+ }),
399
+ expected_resp: Ok(72),
400
+ },
401
+ TestCase {
402
+ name: "buf len matches entry len",
403
+ key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
404
+ value: 1.0,
405
+ buf_len: 80,
406
+ expected_entry: Some(TestEntry {
407
+ header: 61,
408
+ json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
409
+ padding_len: 7,
410
+ value: 1.0,
411
+ }),
412
+ expected_resp: Ok(72),
413
+ },
414
+ TestCase {
415
+ name: "buf much too short",
416
+ key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
417
+ value: 1.0,
418
+ buf_len: 5,
419
+ expected_entry: None,
420
+ expected_resp: Err(MmapError::Other(format!(
421
+ "entry length {} larger than slice length {}",
422
+ 80, 5,
423
+ ))),
424
+ },
425
+ TestCase {
426
+ name: "buf short by one",
427
+ key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
428
+ value: 1.0,
429
+ buf_len: 79,
430
+ expected_entry: None,
431
+ expected_resp: Err(MmapError::Other(format!(
432
+ "entry length {} larger than slice length {}",
433
+ 80, 79,
434
+ ))),
435
+ },
436
+ ];
437
+
438
+ for case in tc {
439
+ let mut buf = vec![0; case.buf_len];
440
+ let resp = RawEntry::save(&mut buf, case.key, case.value);
441
+
442
+ assert_eq!(
443
+ case.expected_resp, resp,
444
+ "test case: {} - response",
445
+ case.name,
446
+ );
447
+
448
+ if let Some(e) = case.expected_entry {
449
+ let expected_buf = e.as_bstring();
450
+
451
+ assert_eq!(
452
+ expected_buf,
453
+ buf[..expected_buf.len()].as_bstr(),
454
+ "test case: {} - buffer state",
455
+ case.name
456
+ );
457
+
458
+ let header_len = u32::from_ne_bytes(buf[..size_of::<u32>()].try_into().unwrap());
459
+ assert_eq!(
460
+ case.key.len(),
461
+ header_len as usize,
462
+ "test case: {} - size header",
463
+ case.name,
464
+ );
465
+ }
466
+ }
467
+ }
468
+
469
+ #[test]
470
+ fn test_calc_value_offset() {
471
+ struct TestCase {
472
+ name: &'static str,
473
+ encoded_len: usize,
474
+ expected_value_offset: Option<usize>,
475
+ expected_total_len: Option<usize>,
476
+ expected_err: Option<MmapError>,
477
+ }
478
+
479
+ let tc = vec![
480
+ TestCase {
481
+ name: "ok",
482
+ encoded_len: 8,
483
+ expected_value_offset: Some(16),
484
+ expected_total_len: Some(24),
485
+ expected_err: None,
486
+ },
487
+ TestCase {
488
+ name: "padding length one",
489
+ encoded_len: 3,
490
+ expected_value_offset: Some(8),
491
+ expected_total_len: Some(16),
492
+ expected_err: None,
493
+ },
494
+ TestCase {
495
+ name: "padding length eight",
496
+ encoded_len: 4,
497
+ expected_value_offset: Some(16),
498
+ expected_total_len: Some(24),
499
+ expected_err: None,
500
+ },
501
+ TestCase {
502
+ name: "encoded len gt i32::MAX",
503
+ encoded_len: i32::MAX as usize + 1,
504
+ expected_value_offset: None,
505
+ expected_total_len: None,
506
+ expected_err: Some(MmapError::KeyLength),
507
+ },
508
+ ];
509
+
510
+ for case in tc {
511
+ let name = case.name;
512
+ if let Some(expected_value_offset) = case.expected_value_offset {
513
+ assert_eq!(
514
+ expected_value_offset,
515
+ RawEntry::calc_value_offset(case.encoded_len).unwrap(),
516
+ "test case: {name} - value offset"
517
+ );
518
+ }
519
+
520
+ if let Some(expected_total_len) = case.expected_total_len {
521
+ assert_eq!(
522
+ expected_total_len,
523
+ RawEntry::calc_total_len(case.encoded_len).unwrap(),
524
+ "test case: {name} - total len"
525
+ );
526
+ }
527
+
528
+ if let Some(expected_err) = case.expected_err {
529
+ assert_eq!(
530
+ expected_err,
531
+ RawEntry::calc_value_offset(case.encoded_len).unwrap_err(),
532
+ "test case: {name} - err"
533
+ );
534
+ }
535
+ }
536
+ }
537
+
538
+ #[test]
539
+ fn test_padding_len() {
540
+ for encoded_len in 0..64 {
541
+ let padding = RawEntry::padding_len(encoded_len);
542
+
543
+ // Validate we're actually aligning to 8 bytes.
544
+ assert!((size_of::<u32>() + encoded_len + padding) % 8 == 0)
545
+ }
546
+ }
547
+ }