prometheus-client-mmap 0.22.0-x86_64-linux-musl → 1.2.1-x86_64-linux-musl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
@@ -166,7 +166,7 @@ impl EntryMap {
|
|
166
166
|
|
167
167
|
#[cfg(test)]
|
168
168
|
mod test {
|
169
|
-
use magnus::
|
169
|
+
use magnus::Symbol;
|
170
170
|
use std::mem;
|
171
171
|
|
172
172
|
use super::*;
|
@@ -197,7 +197,7 @@ mod test {
|
|
197
197
|
},
|
198
198
|
meta: EntryMetadata {
|
199
199
|
multiprocess_mode: Symbol::new("max"),
|
200
|
-
type_:
|
200
|
+
type_: Symbol::new("gauge"),
|
201
201
|
value: 1.0,
|
202
202
|
},
|
203
203
|
},
|
@@ -208,7 +208,7 @@ mod test {
|
|
208
208
|
},
|
209
209
|
meta: EntryMetadata {
|
210
210
|
multiprocess_mode: Symbol::new("max"),
|
211
|
-
type_:
|
211
|
+
type_: Symbol::new("gauge"),
|
212
212
|
value: 1.0,
|
213
213
|
},
|
214
214
|
},
|
@@ -219,7 +219,7 @@ mod test {
|
|
219
219
|
},
|
220
220
|
meta: EntryMetadata {
|
221
221
|
multiprocess_mode: Symbol::new("max"),
|
222
|
-
type_:
|
222
|
+
type_: Symbol::new("gauge"),
|
223
223
|
value: 1.0,
|
224
224
|
},
|
225
225
|
},
|
@@ -230,7 +230,7 @@ mod test {
|
|
230
230
|
},
|
231
231
|
meta: EntryMetadata {
|
232
232
|
multiprocess_mode: Symbol::new("max"),
|
233
|
-
type_:
|
233
|
+
type_: Symbol::new("gauge"),
|
234
234
|
value: 1.0,
|
235
235
|
},
|
236
236
|
},
|
@@ -241,7 +241,7 @@ mod test {
|
|
241
241
|
},
|
242
242
|
meta: EntryMetadata {
|
243
243
|
multiprocess_mode: Symbol::new("all"),
|
244
|
-
type_:
|
244
|
+
type_: Symbol::new("gauge"),
|
245
245
|
value: 1.0,
|
246
246
|
},
|
247
247
|
},
|
@@ -252,7 +252,7 @@ mod test {
|
|
252
252
|
},
|
253
253
|
meta: EntryMetadata {
|
254
254
|
multiprocess_mode: Symbol::new("all"),
|
255
|
-
type_:
|
255
|
+
type_: Symbol::new("gauge"),
|
256
256
|
value: 1.0,
|
257
257
|
},
|
258
258
|
},
|
@@ -293,7 +293,7 @@ mod test {
|
|
293
293
|
},
|
294
294
|
meta: EntryMetadata {
|
295
295
|
multiprocess_mode: Symbol::new("all"),
|
296
|
-
type_:
|
296
|
+
type_: Symbol::new("gauge"),
|
297
297
|
value: 1.0,
|
298
298
|
},
|
299
299
|
};
|
@@ -305,7 +305,7 @@ mod test {
|
|
305
305
|
},
|
306
306
|
meta: EntryMetadata {
|
307
307
|
multiprocess_mode: Symbol::new("all"),
|
308
|
-
type_:
|
308
|
+
type_: Symbol::new("gauge"),
|
309
309
|
value: 5.0,
|
310
310
|
},
|
311
311
|
};
|
@@ -317,7 +317,7 @@ mod test {
|
|
317
317
|
},
|
318
318
|
meta: EntryMetadata {
|
319
319
|
multiprocess_mode: Symbol::new("all"),
|
320
|
-
type_:
|
320
|
+
type_: Symbol::new("gauge"),
|
321
321
|
value: 100.0,
|
322
322
|
},
|
323
323
|
};
|
@@ -329,7 +329,7 @@ mod test {
|
|
329
329
|
},
|
330
330
|
meta: EntryMetadata {
|
331
331
|
multiprocess_mode: Symbol::new("all"),
|
332
|
-
type_:
|
332
|
+
type_: Symbol::new("gauge"),
|
333
333
|
value: 1.0,
|
334
334
|
},
|
335
335
|
};
|
@@ -460,7 +460,7 @@ mod test {
|
|
460
460
|
path,
|
461
461
|
len: case.json.len(),
|
462
462
|
multiprocess_mode: Symbol::new("max"),
|
463
|
-
type_:
|
463
|
+
type_: Symbol::new("gauge"),
|
464
464
|
pid: "worker-1".to_string(),
|
465
465
|
};
|
466
466
|
|
@@ -204,6 +204,12 @@ impl InnerMmap {
|
|
204
204
|
self.map.as_ptr()
|
205
205
|
}
|
206
206
|
|
207
|
+
/// Returns a mutable raw pointer to the mmap.
|
208
|
+
/// For use in updating RString internals which requires a mutable pointer.
|
209
|
+
pub fn as_mut_ptr(&self) -> *mut u8 {
|
210
|
+
self.map.as_ptr().cast_mut()
|
211
|
+
}
|
212
|
+
|
207
213
|
/// Perform an msync(2) on the mmap, flushing all changes written
|
208
214
|
/// to disk. The sync may optionally be performed asynchronously.
|
209
215
|
pub fn flush(&mut self, f_async: bool) -> Result<()> {
|
@@ -218,16 +224,6 @@ impl InnerMmap {
|
|
218
224
|
}
|
219
225
|
}
|
220
226
|
|
221
|
-
/// Truncate the mmapped file to the end of the metrics data.
|
222
|
-
pub fn truncate_file(&mut self) -> Result<()> {
|
223
|
-
// CAST: no-op on 64-bit, widening on 32-bit.
|
224
|
-
let trunc_len = self.len as u64;
|
225
|
-
|
226
|
-
self.file
|
227
|
-
.set_len(trunc_len)
|
228
|
-
.map_err(|e| MmapError::legacy(format!("truncate: {e}"), RubyError::Type))
|
229
|
-
}
|
230
|
-
|
231
227
|
/// Load the `used` header containing the size of the metrics data written.
|
232
228
|
pub fn load_used(&self) -> Result<u32> {
|
233
229
|
match read_u32(self.map.as_ref(), 0) {
|
@@ -3,7 +3,8 @@ use magnus::prelude::*;
|
|
3
3
|
use magnus::rb_sys::{AsRawValue, FromRawValue};
|
4
4
|
use magnus::typed_data::Obj;
|
5
5
|
use magnus::value::Fixnum;
|
6
|
-
use magnus::{
|
6
|
+
use magnus::{eval, scan_args, Error, Integer, RArray, RClass, RHash, RString, Value};
|
7
|
+
use nix::libc::{c_char, c_long, c_ulong};
|
7
8
|
use rb_sys::rb_str_new_static;
|
8
9
|
use std::fs::File;
|
9
10
|
use std::io::{prelude::*, SeekFrom};
|
@@ -24,6 +25,18 @@ use inner::InnerMmap;
|
|
24
25
|
|
25
26
|
mod inner;
|
26
27
|
|
28
|
+
#[cfg(ruby_gte_3_4)]
|
29
|
+
/// The Ruby `STR_SHARED` flag, aka `FL_USER0`.
|
30
|
+
/// This was changed from `FL_USER2` in https://github.com/ruby/ruby/commit/6deeec5d459ecff5ec4628523b14ac7379fd942e.
|
31
|
+
const STR_SHARED: c_ulong = 1 << (12);
|
32
|
+
|
33
|
+
#[cfg(ruby_lte_3_3)]
|
34
|
+
/// The Ruby `STR_SHARED` flag, aka `FL_USER2`.
|
35
|
+
const STR_SHARED: c_ulong = 1 << (14);
|
36
|
+
|
37
|
+
/// The Ruby `STR_NOEMBED` flag, aka `FL_USER1`.
|
38
|
+
const STR_NOEMBED: c_ulong = 1 << (13);
|
39
|
+
|
27
40
|
/// A Rust struct wrapped in a Ruby object, providing access to a memory-mapped
|
28
41
|
/// file used to store, update, and read out Prometheus metrics.
|
29
42
|
///
|
@@ -150,13 +163,28 @@ impl MmapedFile {
|
|
150
163
|
///
|
151
164
|
/// return a substring of <em>lenght</em> characters from <em>start</em>
|
152
165
|
pub fn slice(rb_self: Obj<Self>, args: &[Value]) -> magnus::error::Result<RString> {
|
153
|
-
// The C
|
166
|
+
// The C implementation would trigger a GC cycle via `rb_gc_force_recycle`
|
154
167
|
// if the `MM_PROTECT` flag is set, but in practice this is never used.
|
155
168
|
// We omit this logic, particularly because `rb_gc_force_recycle` is a
|
156
169
|
// no-op as of Ruby 3.1.
|
170
|
+
let rs_self = &*rb_self;
|
157
171
|
|
158
|
-
let str =
|
159
|
-
|
172
|
+
let str = rs_self.str(rb_self)?;
|
173
|
+
rs_self._slice(rb_self, str, args)
|
174
|
+
}
|
175
|
+
|
176
|
+
fn _slice(
|
177
|
+
&self,
|
178
|
+
rb_self: Obj<Self>,
|
179
|
+
str: RString,
|
180
|
+
args: &[Value],
|
181
|
+
) -> magnus::error::Result<RString> {
|
182
|
+
let substr: RString = str.funcall("[]", args)?;
|
183
|
+
|
184
|
+
// Track shared child strings which use the same backing storage.
|
185
|
+
if Self::rb_string_is_shared(substr) {
|
186
|
+
(*rb_self).track_rstring(rb_self, substr)?;
|
187
|
+
}
|
160
188
|
|
161
189
|
// The C implementation does this, perhaps to validate that the len we
|
162
190
|
// provided is actually being used.
|
@@ -165,7 +193,7 @@ impl MmapedFile {
|
|
165
193
|
Ok(())
|
166
194
|
})?;
|
167
195
|
|
168
|
-
|
196
|
+
Ok(substr)
|
169
197
|
}
|
170
198
|
|
171
199
|
/// Document-method: msync
|
@@ -204,9 +232,6 @@ impl MmapedFile {
|
|
204
232
|
let rs_self = &*rb_self;
|
205
233
|
|
206
234
|
rs_self.inner_mut(|inner| {
|
207
|
-
// truncate file to actual used size
|
208
|
-
inner.truncate_file()?;
|
209
|
-
|
210
235
|
// We are about to release the backing mmap for Ruby's String
|
211
236
|
// objects. If Ruby attempts to read from them the program will
|
212
237
|
// segfault. We update the length of all Strings to zero so Ruby
|
@@ -222,7 +247,8 @@ impl MmapedFile {
|
|
222
247
|
})?;
|
223
248
|
|
224
249
|
// Update each String object to be zero-length.
|
225
|
-
rs_self.
|
250
|
+
let cap = util::cast_chk::<_, c_long>(rs_self.capacity(), "capacity")?;
|
251
|
+
rs_self.update_weak_map(rb_self, rs_self.as_mut_ptr(), cap)?;
|
226
252
|
|
227
253
|
// Remove the `InnerMmap` from the `RwLock`. This will drop
|
228
254
|
// end of this function, unmapping and closing the file.
|
@@ -336,44 +362,44 @@ impl MmapedFile {
|
|
336
362
|
|
337
363
|
// SAFETY: This is safe so long as the data provided to Ruby meets its
|
338
364
|
// requirements. When unmapping the file this will no longer be the
|
339
|
-
// case, see the comment on `munmap` how we handle this.
|
365
|
+
// case, see the comment on `munmap` for how we handle this.
|
340
366
|
Ok(unsafe { rb_str_new_static(ptr as _, len as _) })
|
341
367
|
})?;
|
342
368
|
|
369
|
+
// SAFETY: We know that rb_str_new_static returns a VALUE.
|
343
370
|
let val = unsafe { Value::from_raw(val_id) };
|
344
371
|
|
345
372
|
// UNWRAP: We created this value as a string above.
|
346
373
|
let str = RString::from_value(val).unwrap();
|
347
374
|
|
348
|
-
|
375
|
+
// Freeze the root string so it can't be mutated out from under any
|
376
|
+
// substrings created. This object is never exposed to callers.
|
377
|
+
str.freeze();
|
349
378
|
|
350
|
-
//
|
351
|
-
|
352
|
-
|
379
|
+
// Track the RString in our `WeakMap` so we can update its address if
|
380
|
+
// we re-mmap the backing file.
|
381
|
+
(*rb_self).track_rstring(rb_self, str)?;
|
353
382
|
|
354
383
|
Ok(str)
|
355
384
|
}
|
356
385
|
|
357
386
|
/// If we reallocate, any live Ruby strings provided by the `str()` method
|
358
|
-
/// will be
|
359
|
-
///
|
360
|
-
fn update_weak_map(
|
387
|
+
/// will be invalidated. We need to iterate over them using and update their
|
388
|
+
/// heap pointers to the newly allocated memory region.
|
389
|
+
fn update_weak_map(
|
390
|
+
&self,
|
391
|
+
rb_self: Obj<Self>,
|
392
|
+
old_ptr: *const c_char,
|
393
|
+
old_cap: c_long,
|
394
|
+
) -> magnus::error::Result<()> {
|
361
395
|
let tracker: Value = rb_self.ivar_get("@weak_obj_tracker")?;
|
362
396
|
|
363
|
-
let
|
364
|
-
// Pointers are not `Send`, convert it to usize to allow capture in closure.
|
365
|
-
let new_ptr = inner.as_ptr() as usize;
|
366
|
-
let new_len = util::cast_chk::<_, i64>(inner.len(), "mmap len")?;
|
367
|
-
|
368
|
-
Ok((new_ptr, new_len))
|
369
|
-
})?;
|
397
|
+
let new_len = self.inner(|inner| util::cast_chk::<_, c_long>(inner.len(), "mmap len"))?;
|
370
398
|
|
371
|
-
//
|
372
|
-
|
373
|
-
let
|
374
|
-
|
375
|
-
.ok_or_else(|| err!(arg_error(), "no argument received from `each_value`"))?;
|
376
|
-
let str = RString::from_value(*val)
|
399
|
+
// Iterate over the values of the `WeakMap`.
|
400
|
+
for val in tracker.enumeratorize("each_value", ()) {
|
401
|
+
let rb_string = val?;
|
402
|
+
let str = RString::from_value(rb_string)
|
377
403
|
.ok_or_else(|| err!(arg_error(), "weakmap value was not a string"))?;
|
378
404
|
|
379
405
|
// SAFETY: We're messing with Ruby's internals here, YOLO.
|
@@ -382,18 +408,42 @@ impl MmapedFile {
|
|
382
408
|
// which provides access to its internals.
|
383
409
|
let mut raw_str = Self::rb_string_internal(str);
|
384
410
|
|
411
|
+
// Shared string have their own `ptr` and `len` values, but `aux`
|
412
|
+
// is the id of the parent string so the GC can track this
|
413
|
+
// dependency. The `ptr` will always be an offset from the base
|
414
|
+
// address of the mmap, and `len` will be the length of the mmap
|
415
|
+
// less the offset from the base.
|
416
|
+
if Self::rb_string_is_shared(str) && new_len > 0 {
|
417
|
+
// Calculate how far into the original mmap the shared string
|
418
|
+
// started and update to the equivalent address in the new
|
419
|
+
// one.
|
420
|
+
let substr_ptr = raw_str.as_ref().as_.heap.ptr;
|
421
|
+
let offset = substr_ptr.offset_from(old_ptr);
|
422
|
+
|
423
|
+
raw_str.as_mut().as_.heap.ptr = self.as_mut_ptr().offset(offset);
|
424
|
+
|
425
|
+
let current_len = str.len() as c_long;
|
426
|
+
let new_shared_len = old_cap + current_len;
|
427
|
+
|
428
|
+
self.update_rstring_len(raw_str, new_shared_len);
|
429
|
+
continue;
|
430
|
+
}
|
431
|
+
|
385
432
|
// Update the string to point to the new mmapped file.
|
386
433
|
// We're matching the behavior of Ruby's `str_new_static` function.
|
387
434
|
// See https://github.com/ruby/ruby/blob/e51014f9c05aa65cbf203442d37fef7c12390015/string.c#L1030-L1053
|
388
|
-
|
389
|
-
|
390
|
-
|
435
|
+
//
|
436
|
+
// We deliberately do _NOT_ increment the `capa` field of the
|
437
|
+
// string to match the new `len`. We were initially doing this,
|
438
|
+
// but consistently triggered GCs in the middle of updating the
|
439
|
+
// string pointers, causing a segfault.
|
440
|
+
//
|
441
|
+
// See https://gitlab.com/gitlab-org/ruby/gems/prometheus-client-mmap/-/issues/45
|
442
|
+
raw_str.as_mut().as_.heap.ptr = self.as_mut_ptr();
|
443
|
+
self.update_rstring_len(raw_str, new_len);
|
391
444
|
}
|
392
|
-
|
393
|
-
});
|
445
|
+
}
|
394
446
|
|
395
|
-
// Execute the block.
|
396
|
-
let _: Value = tracker.funcall_with_block("each_value", (), block)?;
|
397
447
|
Ok(())
|
398
448
|
}
|
399
449
|
|
@@ -406,8 +456,7 @@ impl MmapedFile {
|
|
406
456
|
|
407
457
|
// We need the mmapped region to contain at least one byte beyond the
|
408
458
|
// written data to create a NUL- terminated C string. Validate that
|
409
|
-
// new length does not exactly match the length of the mmap
|
410
|
-
// it.
|
459
|
+
// new length does not exactly match or exceed the length of the mmap.
|
411
460
|
while self.capacity() <= used.add_chk(entry_len)? {
|
412
461
|
self.expand_to_fit(rb_self, self.capacity().mul_chk(2)?)?;
|
413
462
|
}
|
@@ -429,6 +478,9 @@ impl MmapedFile {
|
|
429
478
|
}
|
430
479
|
|
431
480
|
if new_cap != self.capacity() {
|
481
|
+
let old_ptr = self.as_mut_ptr();
|
482
|
+
let old_cap = util::cast_chk::<_, c_long>(self.capacity(), "capacity")?;
|
483
|
+
|
432
484
|
// Drop the old mmap.
|
433
485
|
let (mut file, path) = self.take_inner()?.munmap();
|
434
486
|
|
@@ -439,7 +491,7 @@ impl MmapedFile {
|
|
439
491
|
|
440
492
|
self.insert_inner(new_inner)?;
|
441
493
|
|
442
|
-
return self.update_weak_map(rb_self);
|
494
|
+
return self.update_weak_map(rb_self, old_ptr, old_cap);
|
443
495
|
}
|
444
496
|
|
445
497
|
Ok(())
|
@@ -459,14 +511,14 @@ impl MmapedFile {
|
|
459
511
|
match file.seek(SeekFrom::Start(len - 1)) {
|
460
512
|
Ok(_) => {}
|
461
513
|
Err(_) => {
|
462
|
-
return Err(MmapError::
|
514
|
+
return Err(MmapError::with_errno(format!("Can't lseek {}", len - 1)));
|
463
515
|
}
|
464
516
|
}
|
465
517
|
|
466
518
|
match file.write(&[0x0]) {
|
467
519
|
Ok(1) => {}
|
468
520
|
_ => {
|
469
|
-
return Err(MmapError::
|
521
|
+
return Err(MmapError::with_errno(format!(
|
470
522
|
"Can't extend {}",
|
471
523
|
path.display()
|
472
524
|
)));
|
@@ -476,6 +528,15 @@ impl MmapedFile {
|
|
476
528
|
Ok(())
|
477
529
|
}
|
478
530
|
|
531
|
+
fn track_rstring(&self, rb_self: Obj<Self>, str: RString) -> magnus::error::Result<()> {
|
532
|
+
let tracker: Value = rb_self.ivar_get("@weak_obj_tracker")?;
|
533
|
+
|
534
|
+
// Use the string's Id as the key in the `WeakMap`.
|
535
|
+
let key = str.as_raw();
|
536
|
+
let _: Value = tracker.funcall("[]=", (key, str))?;
|
537
|
+
Ok(())
|
538
|
+
}
|
539
|
+
|
479
540
|
/// The total capacity of the underlying mmap.
|
480
541
|
#[inline]
|
481
542
|
fn capacity(&self) -> usize {
|
@@ -489,6 +550,13 @@ impl MmapedFile {
|
|
489
550
|
.map_err(|e| e.into())
|
490
551
|
}
|
491
552
|
|
553
|
+
fn as_mut_ptr(&self) -> *mut c_char {
|
554
|
+
// UNWRAP: This is actually infallible, but we need to
|
555
|
+
// wrap it in a `Result` for use with `inner()`.
|
556
|
+
self.inner(|inner| Ok(inner.as_mut_ptr() as *mut c_char))
|
557
|
+
.unwrap()
|
558
|
+
}
|
559
|
+
|
492
560
|
/// Takes a closure with immutable access to InnerMmap. Will fail if the inner
|
493
561
|
/// object has a mutable borrow or has been dropped.
|
494
562
|
fn inner<F, T>(&self, func: F) -> Result<T>
|
@@ -544,12 +612,40 @@ impl MmapedFile {
|
|
544
612
|
Ok(())
|
545
613
|
}
|
546
614
|
|
615
|
+
/// Check if an RString is shared. Shared string use the same underlying
|
616
|
+
/// storage as their parent, taking an offset from the start. By default
|
617
|
+
/// they must run to the end of the parent string.
|
618
|
+
fn rb_string_is_shared(rb_str: RString) -> bool {
|
619
|
+
// SAFETY: We only hold a reference to the raw object for the duration
|
620
|
+
// of this function, and no Ruby code is called.
|
621
|
+
let flags = unsafe {
|
622
|
+
let raw_str = Self::rb_string_internal(rb_str);
|
623
|
+
raw_str.as_ref().basic.flags
|
624
|
+
};
|
625
|
+
let shared_flags = STR_SHARED | STR_NOEMBED;
|
626
|
+
|
627
|
+
flags & shared_flags == shared_flags
|
628
|
+
}
|
629
|
+
|
547
630
|
/// Convert `magnus::RString` into the raw binding used by `rb_sys::RString`.
|
548
631
|
/// We need this to manually change the pointer and length values for strings
|
549
632
|
/// when moving the mmap to a new file.
|
633
|
+
///
|
634
|
+
/// SAFETY: Calling Ruby code while the returned object is held may result
|
635
|
+
/// in it being mutated or dropped.
|
550
636
|
unsafe fn rb_string_internal(rb_str: RString) -> NonNull<rb_sys::RString> {
|
551
637
|
mem::transmute::<RString, NonNull<rb_sys::RString>>(rb_str)
|
552
638
|
}
|
639
|
+
|
640
|
+
#[cfg(ruby_lte_3_2)]
|
641
|
+
unsafe fn update_rstring_len(&self, mut raw_str: NonNull<rb_sys::RString>, new_len: c_long) {
|
642
|
+
raw_str.as_mut().as_.heap.len = new_len;
|
643
|
+
}
|
644
|
+
|
645
|
+
#[cfg(ruby_gte_3_3)]
|
646
|
+
unsafe fn update_rstring_len(&self, mut raw_str: NonNull<rb_sys::RString>, new_len: c_long) {
|
647
|
+
raw_str.as_mut().len = new_len;
|
648
|
+
}
|
553
649
|
}
|
554
650
|
|
555
651
|
#[cfg(test)]
|
@@ -666,18 +762,100 @@ mod test {
|
|
666
762
|
let ruby = magnus::Ruby::get().unwrap();
|
667
763
|
crate::init(&ruby).unwrap();
|
668
764
|
|
765
|
+
fn assert_internals(
|
766
|
+
obj: Obj<MmapedFile>,
|
767
|
+
parent_id: c_ulong,
|
768
|
+
child_id: c_ulong,
|
769
|
+
unshared_id: c_ulong,
|
770
|
+
) {
|
771
|
+
let rs_self = &*obj;
|
772
|
+
let tracker: Value = obj.ivar_get("@weak_obj_tracker").unwrap();
|
773
|
+
|
774
|
+
let mmap_ptr = rs_self.as_mut_ptr();
|
775
|
+
let mmap_len = rs_self.capacity();
|
776
|
+
|
777
|
+
let mut parent_checked = false;
|
778
|
+
let mut child_checked = false;
|
779
|
+
|
780
|
+
for val in tracker.enumeratorize("each_value", ()) {
|
781
|
+
let rb_string = val.unwrap();
|
782
|
+
let str = RString::from_value(rb_string).unwrap();
|
783
|
+
|
784
|
+
unsafe {
|
785
|
+
let raw_str = MmapedFile::rb_string_internal(str);
|
786
|
+
if str.as_raw() == child_id {
|
787
|
+
assert_eq!(parent_id, raw_str.as_ref().as_.heap.aux.shared);
|
788
|
+
|
789
|
+
let child_offset = mmap_len as isize - str.len() as isize;
|
790
|
+
assert_eq!(mmap_ptr.offset(child_offset), raw_str.as_ref().as_.heap.ptr);
|
791
|
+
|
792
|
+
child_checked = true;
|
793
|
+
} else if str.as_raw() == parent_id {
|
794
|
+
assert_eq!(parent_id, str.as_raw());
|
795
|
+
|
796
|
+
assert_eq!(mmap_ptr, raw_str.as_ref().as_.heap.ptr);
|
797
|
+
assert_eq!(mmap_len as c_long, str.len() as c_long);
|
798
|
+
assert!(raw_str.as_ref().basic.flags & (STR_SHARED | STR_NOEMBED) > 0);
|
799
|
+
assert!(str.is_frozen());
|
800
|
+
|
801
|
+
parent_checked = true;
|
802
|
+
} else if str.as_raw() == unshared_id {
|
803
|
+
panic!("tracking unshared string");
|
804
|
+
} else {
|
805
|
+
panic!("unknown string");
|
806
|
+
}
|
807
|
+
}
|
808
|
+
}
|
809
|
+
assert!(parent_checked && child_checked);
|
810
|
+
}
|
811
|
+
|
669
812
|
let obj = create_obj();
|
670
813
|
let _ = populate_entries(&obj);
|
671
814
|
|
672
815
|
let rs_self = &*obj;
|
673
816
|
|
674
|
-
|
675
|
-
let
|
676
|
-
|
677
|
-
|
817
|
+
// Create a string containing the full mmap.
|
818
|
+
let parent_str = rs_self.str(obj).unwrap();
|
819
|
+
let parent_id = parent_str.as_raw();
|
820
|
+
|
821
|
+
// Ruby's shared strings are only created when they go to the end of
|
822
|
+
// original string.
|
823
|
+
let len = rs_self.inner(|inner| Ok(inner.len())).unwrap();
|
824
|
+
let shareable_range = Range::new(1, len - 1, false).unwrap().as_value();
|
825
|
+
|
826
|
+
// This string should re-use the parent's buffer with an offset and have
|
827
|
+
// the parent's id in `as.heap.aux.shared`
|
828
|
+
let child_str = rs_self._slice(obj, parent_str, &[shareable_range]).unwrap();
|
829
|
+
let child_id = child_str.as_raw();
|
830
|
+
|
831
|
+
// A range that does not reach the end of the parent will not be shared.
|
832
|
+
assert!(len > 4);
|
833
|
+
let unshareable_range = Range::new(0, 4, false).unwrap().as_value();
|
834
|
+
|
835
|
+
// This string should NOT be tracked, it should own its own buffer.
|
836
|
+
let unshared_str = rs_self
|
837
|
+
._slice(obj, parent_str, &[unshareable_range])
|
838
|
+
.unwrap();
|
839
|
+
let unshared_id = unshared_str.as_raw();
|
840
|
+
assert!(!MmapedFile::rb_string_is_shared(unshared_str));
|
841
|
+
|
842
|
+
assert_internals(obj, parent_id, child_id, unshared_id);
|
843
|
+
|
844
|
+
let orig_ptr = rs_self.as_mut_ptr();
|
845
|
+
// Expand a bunch to ensure we remap
|
846
|
+
for _ in 0..16 {
|
847
|
+
rs_self.expand_to_fit(obj, rs_self.capacity() * 2).unwrap();
|
848
|
+
}
|
849
|
+
let new_ptr = rs_self.as_mut_ptr();
|
850
|
+
assert!(orig_ptr != new_ptr);
|
678
851
|
|
679
852
|
// If we haven't updated the pointer to the newly remapped file this will segfault.
|
680
|
-
let _: Value = eval!("puts
|
853
|
+
let _: Value = eval!("puts parent", parent = parent_str).unwrap();
|
854
|
+
let _: Value = eval!("puts child", child = child_str).unwrap();
|
855
|
+
let _: Value = eval!("puts unshared", unshared = unshared_str).unwrap();
|
856
|
+
|
857
|
+
// Confirm that tracked strings are still valid.
|
858
|
+
assert_internals(obj, parent_id, child_id, unshared_id);
|
681
859
|
}
|
682
860
|
|
683
861
|
#[test]
|
@@ -6,7 +6,7 @@ use crate::util::CheckedOps;
|
|
6
6
|
use crate::Result;
|
7
7
|
|
8
8
|
/// The logic to save a `MetricsEntry`, or parse one from a byte slice.
|
9
|
-
#[derive(PartialEq, Clone, Debug)]
|
9
|
+
#[derive(PartialEq, Eq, Clone, Debug)]
|
10
10
|
pub struct RawEntry<'a> {
|
11
11
|
bytes: &'a [u8],
|
12
12
|
encoded_len: usize,
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -26,49 +26,16 @@ module Prometheus
|
|
26
26
|
Helper::MetricsRepresentation.to_text(metrics)
|
27
27
|
end
|
28
28
|
|
29
|
-
def marshal_multiprocess(path = Prometheus::Client.configuration.multiprocess_files_dir
|
29
|
+
def marshal_multiprocess(path = Prometheus::Client.configuration.multiprocess_files_dir)
|
30
30
|
file_list = Dir.glob(File.join(path, '*.db')).sort
|
31
31
|
.map {|f| Helper::PlainFile.new(f) }
|
32
32
|
.map {|f| [f.filepath, f.multiprocess_mode.to_sym, f.type.to_sym, f.pid] }
|
33
33
|
|
34
|
-
if use_rust && rust_impl_available?
|
35
34
|
FastMmapedFileRs.to_metrics(file_list.to_a)
|
36
|
-
else
|
37
|
-
FastMmapedFile.to_metrics(file_list.to_a)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def rust_impl_available?
|
42
|
-
return @rust_available unless @rust_available.nil?
|
43
|
-
|
44
|
-
check_for_rust
|
45
35
|
end
|
46
36
|
|
47
37
|
private
|
48
38
|
|
49
|
-
def load_rust_extension
|
50
|
-
begin
|
51
|
-
ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
|
52
|
-
require_relative "../../../#{ruby_version}/fast_mmaped_file_rs"
|
53
|
-
rescue LoadError
|
54
|
-
require 'fast_mmaped_file_rs'
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def check_for_rust
|
59
|
-
# This will be evaluated on each invocation even with `||=` if
|
60
|
-
# `@rust_available` if false. Running a `require` statement is slow,
|
61
|
-
# so the `rust_impl_available?` method memoizes the result, external
|
62
|
-
# callers can only trigger this method a single time.
|
63
|
-
@rust_available = begin
|
64
|
-
load_rust_extension
|
65
|
-
true
|
66
|
-
rescue LoadError
|
67
|
-
Prometheus::Client.logger.info('FastMmapedFileRs unavailable')
|
68
|
-
false
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
39
|
def load_metrics(path)
|
73
40
|
metrics = {}
|
74
41
|
Dir.glob(File.join(path, '*.db')).sort.each do |f|
|
@@ -4,15 +4,15 @@ require 'prometheus/client/helper/file_locker'
|
|
4
4
|
# load precompiled extension if available
|
5
5
|
begin
|
6
6
|
ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
|
7
|
-
require_relative "../../../#{ruby_version}/
|
7
|
+
require_relative "../../../#{ruby_version}/fast_mmaped_file_rs"
|
8
8
|
rescue LoadError
|
9
|
-
require '
|
9
|
+
require 'fast_mmaped_file_rs'
|
10
10
|
end
|
11
11
|
|
12
12
|
module Prometheus
|
13
13
|
module Client
|
14
14
|
module Helper
|
15
|
-
class MmapedFile <
|
15
|
+
class MmapedFile < FastMmapedFileRs
|
16
16
|
include EntryParser
|
17
17
|
|
18
18
|
attr_reader :filepath, :size
|
@@ -5,8 +5,7 @@ module Prometheus
|
|
5
5
|
# LabelSetValidator ensures that all used label sets comply with the
|
6
6
|
# Prometheus specification.
|
7
7
|
class LabelSetValidator
|
8
|
-
|
9
|
-
RESERVED_LABELS = [:job, :instance].freeze
|
8
|
+
RESERVED_LABELS = [].freeze
|
10
9
|
|
11
10
|
class LabelSetError < StandardError; end
|
12
11
|
class InvalidLabelSetError < LabelSetError; end
|