prometheus-client-mmap 0.20.3-x86_64-linux
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +253 -0
- data/ext/fast_mmaped_file/extconf.rb +30 -0
- data/ext/fast_mmaped_file/fast_mmaped_file.c +122 -0
- data/ext/fast_mmaped_file/file_format.c +5 -0
- data/ext/fast_mmaped_file/file_format.h +11 -0
- data/ext/fast_mmaped_file/file_parsing.c +195 -0
- data/ext/fast_mmaped_file/file_parsing.h +27 -0
- data/ext/fast_mmaped_file/file_reading.c +102 -0
- data/ext/fast_mmaped_file/file_reading.h +30 -0
- data/ext/fast_mmaped_file/globals.h +14 -0
- data/ext/fast_mmaped_file/mmap.c +427 -0
- data/ext/fast_mmaped_file/mmap.h +61 -0
- data/ext/fast_mmaped_file/rendering.c +199 -0
- data/ext/fast_mmaped_file/rendering.h +8 -0
- data/ext/fast_mmaped_file/utils.c +56 -0
- data/ext/fast_mmaped_file/utils.h +22 -0
- data/ext/fast_mmaped_file/value_access.c +242 -0
- data/ext/fast_mmaped_file/value_access.h +15 -0
- data/ext/fast_mmaped_file_rs/.cargo/config.toml +23 -0
- data/ext/fast_mmaped_file_rs/Cargo.lock +790 -0
- data/ext/fast_mmaped_file_rs/Cargo.toml +30 -0
- data/ext/fast_mmaped_file_rs/README.md +52 -0
- data/ext/fast_mmaped_file_rs/extconf.rb +30 -0
- data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
- data/ext/fast_mmaped_file_rs/src/file_entry.rs +579 -0
- data/ext/fast_mmaped_file_rs/src/file_info.rs +190 -0
- data/ext/fast_mmaped_file_rs/src/lib.rs +79 -0
- data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
- data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
- data/ext/fast_mmaped_file_rs/src/mmap.rs +151 -0
- data/ext/fast_mmaped_file_rs/src/parser.rs +346 -0
- data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
- data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
- data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
- 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.1/fast_mmaped_file_rs.so +0 -0
- data/lib/3.2/fast_mmaped_file.so +0 -0
- data/lib/3.2/fast_mmaped_file_rs.so +0 -0
- data/lib/prometheus/client/configuration.rb +23 -0
- data/lib/prometheus/client/counter.rb +27 -0
- data/lib/prometheus/client/formats/text.rb +118 -0
- data/lib/prometheus/client/gauge.rb +40 -0
- data/lib/prometheus/client/helper/entry_parser.rb +132 -0
- data/lib/prometheus/client/helper/file_locker.rb +50 -0
- data/lib/prometheus/client/helper/json_parser.rb +23 -0
- data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
- data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
- data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
- data/lib/prometheus/client/helper/plain_file.rb +29 -0
- data/lib/prometheus/client/histogram.rb +80 -0
- data/lib/prometheus/client/label_set_validator.rb +86 -0
- data/lib/prometheus/client/metric.rb +80 -0
- data/lib/prometheus/client/mmaped_dict.rb +79 -0
- data/lib/prometheus/client/mmaped_value.rb +154 -0
- data/lib/prometheus/client/page_size.rb +17 -0
- data/lib/prometheus/client/push.rb +203 -0
- data/lib/prometheus/client/rack/collector.rb +88 -0
- data/lib/prometheus/client/rack/exporter.rb +96 -0
- data/lib/prometheus/client/registry.rb +65 -0
- data/lib/prometheus/client/simple_value.rb +31 -0
- data/lib/prometheus/client/summary.rb +69 -0
- data/lib/prometheus/client/support/unicorn.rb +35 -0
- data/lib/prometheus/client/uses_value_type.rb +20 -0
- data/lib/prometheus/client/version.rb +5 -0
- data/lib/prometheus/client.rb +58 -0
- data/lib/prometheus.rb +3 -0
- data/vendor/c/hashmap/.gitignore +52 -0
- data/vendor/c/hashmap/LICENSE +21 -0
- data/vendor/c/hashmap/README.md +90 -0
- data/vendor/c/hashmap/_config.yml +1 -0
- data/vendor/c/hashmap/src/hashmap.c +692 -0
- data/vendor/c/hashmap/src/hashmap.h +267 -0
- data/vendor/c/hashmap/test/Makefile +22 -0
- data/vendor/c/hashmap/test/hashmap_test.c +608 -0
- data/vendor/c/jsmn/.travis.yml +4 -0
- data/vendor/c/jsmn/LICENSE +20 -0
- data/vendor/c/jsmn/Makefile +41 -0
- data/vendor/c/jsmn/README.md +168 -0
- data/vendor/c/jsmn/example/jsondump.c +126 -0
- data/vendor/c/jsmn/example/simple.c +76 -0
- data/vendor/c/jsmn/jsmn.c +314 -0
- data/vendor/c/jsmn/jsmn.h +76 -0
- data/vendor/c/jsmn/library.json +16 -0
- data/vendor/c/jsmn/test/test.h +27 -0
- data/vendor/c/jsmn/test/tests.c +407 -0
- data/vendor/c/jsmn/test/testutil.h +94 -0
- metadata +243 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'prometheus/client/helper/entry_parser'
|
2
|
+
require 'prometheus/client/helper/file_locker'
|
3
|
+
|
4
|
+
# load precompiled extension if available
|
5
|
+
begin
|
6
|
+
ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
|
7
|
+
require_relative "../../../#{ruby_version}/fast_mmaped_file"
|
8
|
+
rescue LoadError
|
9
|
+
require 'fast_mmaped_file'
|
10
|
+
end
|
11
|
+
|
12
|
+
module Prometheus
|
13
|
+
module Client
|
14
|
+
module Helper
|
15
|
+
class MmapedFile < FastMmapedFile
|
16
|
+
include EntryParser
|
17
|
+
|
18
|
+
attr_reader :filepath, :size
|
19
|
+
|
20
|
+
def initialize(filepath)
|
21
|
+
@filepath = filepath
|
22
|
+
|
23
|
+
File.open(filepath, 'a+b') do |file|
|
24
|
+
file.truncate(initial_mmap_file_size) if file.size < MINIMUM_SIZE
|
25
|
+
@size = file.size
|
26
|
+
end
|
27
|
+
|
28
|
+
super(filepath)
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
munmap
|
33
|
+
FileLocker.unlock(filepath)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def initial_mmap_file_size
|
39
|
+
Prometheus::Client.configuration.initial_mmap_file_size
|
40
|
+
end
|
41
|
+
|
42
|
+
public
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def open(filepath)
|
46
|
+
MmapedFile.new(filepath)
|
47
|
+
end
|
48
|
+
|
49
|
+
def ensure_exclusive_file(file_prefix = 'mmaped_file')
|
50
|
+
(0..Float::INFINITY).lazy
|
51
|
+
.map { |f_num| "#{file_prefix}_#{Prometheus::Client.pid}-#{f_num}.db" }
|
52
|
+
.map { |filename| File.join(Prometheus::Client.configuration.multiprocess_files_dir, filename) }
|
53
|
+
.find { |path| Helper::FileLocker.lock_to_process(path) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def open_exclusive_file(file_prefix = 'mmaped_file')
|
57
|
+
filename = Helper::MmapedFile.ensure_exclusive_file(file_prefix)
|
58
|
+
open(filename)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'prometheus/client/helper/entry_parser'
|
2
|
+
|
3
|
+
module Prometheus
|
4
|
+
module Client
|
5
|
+
module Helper
|
6
|
+
# Parses DB files without using mmap
|
7
|
+
class PlainFile
|
8
|
+
include EntryParser
|
9
|
+
attr_reader :filepath
|
10
|
+
|
11
|
+
def source
|
12
|
+
@data ||= File.read(filepath, mode: 'rb')
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(filepath)
|
16
|
+
@filepath = filepath
|
17
|
+
end
|
18
|
+
|
19
|
+
def slice(*args)
|
20
|
+
source.slice(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def size
|
24
|
+
source.length
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'prometheus/client/metric'
|
2
|
+
require 'prometheus/client/uses_value_type'
|
3
|
+
|
4
|
+
module Prometheus
|
5
|
+
module Client
|
6
|
+
# A histogram samples observations (usually things like request durations
|
7
|
+
# or response sizes) and counts them in configurable buckets. It also
|
8
|
+
# provides a sum of all observed values.
|
9
|
+
class Histogram < Metric
|
10
|
+
# Value represents the state of a Histogram at a given point.
|
11
|
+
class Value < Hash
|
12
|
+
include UsesValueType
|
13
|
+
attr_accessor :sum, :total, :total_inf
|
14
|
+
|
15
|
+
def initialize(type, name, labels, buckets)
|
16
|
+
@sum = value_object(type, name, "#{name}_sum", labels)
|
17
|
+
@total = value_object(type, name, "#{name}_count", labels)
|
18
|
+
@total_inf = value_object(type, name, "#{name}_bucket", labels.merge(le: "+Inf"))
|
19
|
+
|
20
|
+
buckets.each do |bucket|
|
21
|
+
self[bucket] = value_object(type, name, "#{name}_bucket", labels.merge(le: bucket.to_s))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def observe(value)
|
26
|
+
@sum.increment(value)
|
27
|
+
@total.increment()
|
28
|
+
@total_inf.increment()
|
29
|
+
|
30
|
+
each_key do |bucket|
|
31
|
+
self[bucket].increment() if value <= bucket
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get()
|
36
|
+
hash = {}
|
37
|
+
each_key do |bucket|
|
38
|
+
hash[bucket] = self[bucket].get()
|
39
|
+
end
|
40
|
+
hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# DEFAULT_BUCKETS are the default Histogram buckets. The default buckets
|
45
|
+
# are tailored to broadly measure the response time (in seconds) of a
|
46
|
+
# network service. (From DefBuckets client_golang)
|
47
|
+
DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
|
48
|
+
2.5, 5, 10].freeze
|
49
|
+
|
50
|
+
# Offer a way to manually specify buckets
|
51
|
+
def initialize(name, docstring, base_labels = {},
|
52
|
+
buckets = DEFAULT_BUCKETS)
|
53
|
+
raise ArgumentError, 'Unsorted buckets, typo?' unless sorted? buckets
|
54
|
+
|
55
|
+
@buckets = buckets
|
56
|
+
super(name, docstring, base_labels)
|
57
|
+
end
|
58
|
+
|
59
|
+
def type
|
60
|
+
:histogram
|
61
|
+
end
|
62
|
+
|
63
|
+
def observe(labels, value)
|
64
|
+
label_set = label_set_for(labels)
|
65
|
+
synchronize { @values[label_set].observe(value) }
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def default(labels)
|
71
|
+
# TODO: default function needs to know key of hash info (label names and values)
|
72
|
+
Value.new(type, @name, labels, @buckets)
|
73
|
+
end
|
74
|
+
|
75
|
+
def sorted?(bucket)
|
76
|
+
bucket.each_cons(2).all? { |i, j| i <= j }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Prometheus
|
4
|
+
module Client
|
5
|
+
# LabelSetValidator ensures that all used label sets comply with the
|
6
|
+
# Prometheus specification.
|
7
|
+
class LabelSetValidator
|
8
|
+
# TODO: we might allow setting :instance in the future
|
9
|
+
RESERVED_LABELS = [:job, :instance].freeze
|
10
|
+
|
11
|
+
class LabelSetError < StandardError; end
|
12
|
+
class InvalidLabelSetError < LabelSetError; end
|
13
|
+
class InvalidLabelError < LabelSetError; end
|
14
|
+
class ReservedLabelError < LabelSetError; end
|
15
|
+
|
16
|
+
def initialize(reserved_labels = [])
|
17
|
+
@reserved_labels = (reserved_labels + RESERVED_LABELS).freeze
|
18
|
+
@validated = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?(labels)
|
22
|
+
unless labels.is_a?(Hash)
|
23
|
+
raise InvalidLabelSetError, "#{labels} is not a valid label set"
|
24
|
+
end
|
25
|
+
|
26
|
+
labels.all? do |key, value|
|
27
|
+
validate_symbol(key)
|
28
|
+
validate_name(key)
|
29
|
+
validate_reserved_key(key)
|
30
|
+
validate_value(key, value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate(labels)
|
35
|
+
return labels if @validated.key?(labels.hash)
|
36
|
+
|
37
|
+
valid?(labels)
|
38
|
+
|
39
|
+
unless @validated.empty? || match?(labels, @validated.first.last)
|
40
|
+
raise InvalidLabelSetError, "labels must have the same signature: (#{label_diff(labels, @validated.first.last)})"
|
41
|
+
end
|
42
|
+
|
43
|
+
@validated[labels.hash] = labels
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def label_diff(a, b)
|
49
|
+
"expected keys: #{b.keys.sort}, got: #{a.keys.sort}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def match?(a, b)
|
53
|
+
a.keys.sort == b.keys.sort
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_symbol(key)
|
57
|
+
return true if key.is_a?(Symbol)
|
58
|
+
|
59
|
+
raise InvalidLabelError, "label #{key} is not a symbol"
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_name(key)
|
63
|
+
return true unless key.to_s.start_with?('__')
|
64
|
+
|
65
|
+
raise ReservedLabelError, "label #{key} must not start with __"
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_reserved_key(key)
|
69
|
+
return true unless @reserved_labels.include?(key)
|
70
|
+
|
71
|
+
raise ReservedLabelError, "#{key} is reserved"
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_value(key, value)
|
75
|
+
return true if value.is_a?(String) ||
|
76
|
+
value.is_a?(Numeric) ||
|
77
|
+
value.is_a?(Symbol) ||
|
78
|
+
value.is_a?(FalseClass) ||
|
79
|
+
value.is_a?(TrueClass) ||
|
80
|
+
value.nil?
|
81
|
+
|
82
|
+
raise InvalidLabelError, "#{key} does not contain a valid value (type #{value.class})"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'prometheus/client/label_set_validator'
|
3
|
+
require 'prometheus/client/uses_value_type'
|
4
|
+
|
5
|
+
module Prometheus
|
6
|
+
module Client
|
7
|
+
class Metric
|
8
|
+
include UsesValueType
|
9
|
+
attr_reader :name, :docstring, :base_labels
|
10
|
+
|
11
|
+
def initialize(name, docstring, base_labels = {})
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@validator = case type
|
14
|
+
when :summary
|
15
|
+
LabelSetValidator.new(['quantile'])
|
16
|
+
when :histogram
|
17
|
+
LabelSetValidator.new(['le'])
|
18
|
+
else
|
19
|
+
LabelSetValidator.new
|
20
|
+
end
|
21
|
+
@values = Hash.new { |hash, key| hash[key] = default(key) }
|
22
|
+
|
23
|
+
validate_name(name)
|
24
|
+
validate_docstring(docstring)
|
25
|
+
@validator.valid?(base_labels)
|
26
|
+
|
27
|
+
@name = name
|
28
|
+
@docstring = docstring
|
29
|
+
@base_labels = base_labels
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the value for the given label set
|
33
|
+
def get(labels = {})
|
34
|
+
label_set = label_set_for(labels)
|
35
|
+
@validator.valid?(label_set)
|
36
|
+
|
37
|
+
@values[label_set].get
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns all label sets with their values
|
41
|
+
def values
|
42
|
+
synchronize do
|
43
|
+
@values.each_with_object({}) do |(labels, value), memo|
|
44
|
+
memo[labels] = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def touch_default_value
|
52
|
+
@values[label_set_for({})]
|
53
|
+
end
|
54
|
+
|
55
|
+
def default(labels)
|
56
|
+
value_object(type, @name, @name, labels)
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_name(name)
|
60
|
+
return true if name.is_a?(Symbol)
|
61
|
+
|
62
|
+
raise ArgumentError, 'given name must be a symbol'
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_docstring(docstring)
|
66
|
+
return true if docstring.respond_to?(:empty?) && !docstring.empty?
|
67
|
+
|
68
|
+
raise ArgumentError, 'docstring must be given'
|
69
|
+
end
|
70
|
+
|
71
|
+
def label_set_for(labels)
|
72
|
+
@validator.validate(@base_labels.merge(labels))
|
73
|
+
end
|
74
|
+
|
75
|
+
def synchronize(&block)
|
76
|
+
@mutex.synchronize(&block)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'prometheus/client/helper/mmaped_file'
|
2
|
+
require 'prometheus/client/helper/plain_file'
|
3
|
+
require 'prometheus/client'
|
4
|
+
|
5
|
+
module Prometheus
|
6
|
+
module Client
|
7
|
+
class ParsingError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
# A dict of doubles, backed by an mmapped file.
|
11
|
+
#
|
12
|
+
# The file starts with a 4 byte int, indicating how much of it is used.
|
13
|
+
# Then 4 bytes of padding.
|
14
|
+
# There's then a number of entries, consisting of a 4 byte int which is the
|
15
|
+
# size of the next field, a utf-8 encoded string key, padding to an 8 byte
|
16
|
+
# alignment, and then a 8 byte float which is the value.
|
17
|
+
class MmapedDict
|
18
|
+
attr_reader :m, :used, :positions
|
19
|
+
|
20
|
+
def self.read_all_values(f)
|
21
|
+
Helper::PlainFile.new(f).entries.map do |data, encoded_len, value_offset, _|
|
22
|
+
encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
|
23
|
+
[encoded, value]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(m)
|
28
|
+
@mutex = Mutex.new
|
29
|
+
|
30
|
+
@m = m
|
31
|
+
# @m.mlock # TODO: Ensure memory is locked to RAM
|
32
|
+
|
33
|
+
@positions = {}
|
34
|
+
read_all_positions.each do |key, pos|
|
35
|
+
@positions[key] = pos
|
36
|
+
end
|
37
|
+
rescue StandardError => e
|
38
|
+
raise ParsingError, "exception #{e} while processing metrics file #{path}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def read_value(key)
|
42
|
+
@m.fetch_entry(@positions, key, 0.0)
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_value(key, value)
|
46
|
+
@m.upsert_entry(@positions, key, value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def path
|
50
|
+
@m.filepath if @m
|
51
|
+
end
|
52
|
+
|
53
|
+
def close
|
54
|
+
@m.sync
|
55
|
+
@m.close
|
56
|
+
rescue TypeError => e
|
57
|
+
Prometheus::Client.logger.warn("munmap raised error #{e}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
"#<#{self.class}:0x#{(object_id << 1).to_s(16)}>"
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def init_value(key)
|
67
|
+
@m.add_entry(@positions, key, 0.0)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Yield (key, pos). No locking is performed.
|
71
|
+
def read_all_positions
|
72
|
+
@m.entries.map do |data, encoded_len, _, absolute_pos|
|
73
|
+
encoded, = data.unpack(format('@4A%d', encoded_len))
|
74
|
+
[encoded, absolute_pos]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'prometheus/client'
|
2
|
+
require 'prometheus/client/mmaped_dict'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Prometheus
|
6
|
+
module Client
|
7
|
+
# A float protected by a mutex backed by a per-process mmaped file.
|
8
|
+
class MmapedValue
|
9
|
+
VALUE_LOCK = Mutex.new
|
10
|
+
|
11
|
+
@@files = {}
|
12
|
+
@@pid = -1
|
13
|
+
|
14
|
+
def initialize(type, metric_name, name, labels, multiprocess_mode = '')
|
15
|
+
@file_prefix = type.to_s
|
16
|
+
@metric_name = metric_name
|
17
|
+
@name = name
|
18
|
+
@labels = labels
|
19
|
+
if type == :gauge
|
20
|
+
@file_prefix += '_' + multiprocess_mode.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
@pid = -1
|
24
|
+
|
25
|
+
@mutex = Mutex.new
|
26
|
+
initialize_file
|
27
|
+
end
|
28
|
+
|
29
|
+
def increment(amount = 1)
|
30
|
+
@mutex.synchronize do
|
31
|
+
initialize_file if pid_changed?
|
32
|
+
|
33
|
+
@value += amount
|
34
|
+
write_value(@key, @value)
|
35
|
+
@value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def decrement(amount = 1)
|
40
|
+
increment(-amount)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set(value)
|
44
|
+
@mutex.synchronize do
|
45
|
+
initialize_file if pid_changed?
|
46
|
+
|
47
|
+
@value = value
|
48
|
+
write_value(@key, @value)
|
49
|
+
@value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def get
|
54
|
+
@mutex.synchronize do
|
55
|
+
initialize_file if pid_changed?
|
56
|
+
return @value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def pid_changed?
|
61
|
+
@pid != Process.pid
|
62
|
+
end
|
63
|
+
|
64
|
+
# method needs to be run in VALUE_LOCK mutex
|
65
|
+
def unsafe_reinitialize_file(check_pid = true)
|
66
|
+
unsafe_initialize_file if !check_pid || pid_changed?
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.reset_and_reinitialize
|
70
|
+
VALUE_LOCK.synchronize do
|
71
|
+
@@pid = Process.pid
|
72
|
+
@@files = {}
|
73
|
+
|
74
|
+
ObjectSpace.each_object(MmapedValue).each do |v|
|
75
|
+
v.unsafe_reinitialize_file(false)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.reset_on_pid_change
|
81
|
+
if pid_changed?
|
82
|
+
@@pid = Process.pid
|
83
|
+
@@files = {}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.reinitialize_on_pid_change
|
88
|
+
VALUE_LOCK.synchronize do
|
89
|
+
reset_on_pid_change
|
90
|
+
|
91
|
+
ObjectSpace.each_object(MmapedValue, &:unsafe_reinitialize_file)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.pid_changed?
|
96
|
+
@@pid != Process.pid
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.multiprocess
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def initialize_file
|
106
|
+
VALUE_LOCK.synchronize do
|
107
|
+
unsafe_initialize_file
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def unsafe_initialize_file
|
112
|
+
self.class.reset_on_pid_change
|
113
|
+
|
114
|
+
@pid = Process.pid
|
115
|
+
unless @@files.has_key?(@file_prefix)
|
116
|
+
unless @file.nil?
|
117
|
+
@file.close
|
118
|
+
end
|
119
|
+
mmaped_file = Helper::MmapedFile.open_exclusive_file(@file_prefix)
|
120
|
+
|
121
|
+
@@files[@file_prefix] = MmapedDict.new(mmaped_file)
|
122
|
+
end
|
123
|
+
|
124
|
+
@file = @@files[@file_prefix]
|
125
|
+
@key = rebuild_key
|
126
|
+
|
127
|
+
@value = read_value(@key)
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def rebuild_key
|
132
|
+
keys = @labels.keys.sort
|
133
|
+
values = @labels.values_at(*keys)
|
134
|
+
|
135
|
+
[@metric_name, @name, keys, values].to_json
|
136
|
+
end
|
137
|
+
|
138
|
+
def write_value(key, val)
|
139
|
+
@file.write_value(key, val)
|
140
|
+
rescue StandardError => e
|
141
|
+
Prometheus::Client.logger.warn("writing value to #{@file.path} failed with #{e}")
|
142
|
+
Prometheus::Client.logger.debug(e.backtrace.join("\n"))
|
143
|
+
end
|
144
|
+
|
145
|
+
def read_value(key)
|
146
|
+
@file.read_value(key)
|
147
|
+
rescue StandardError => e
|
148
|
+
Prometheus::Client.logger.warn("reading value from #{@file.path} failed with #{e}")
|
149
|
+
Prometheus::Client.logger.debug(e.backtrace.join("\n"))
|
150
|
+
0
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Prometheus
|
4
|
+
module Client
|
5
|
+
module PageSize
|
6
|
+
def self.page_size(fallback_page_size: 4096)
|
7
|
+
stdout, status = Open3.capture2('getconf PAGESIZE')
|
8
|
+
return fallback_page_size if status.nil? || !status.success?
|
9
|
+
|
10
|
+
page_size = stdout.chomp.to_i
|
11
|
+
return fallback_page_size if page_size <= 0
|
12
|
+
|
13
|
+
page_size
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|