prometheus-client-mmap 0.7.0.beta14 → 0.7.0.beta15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/prometheus/client/formats/text.rb +1 -0
- data/lib/prometheus/client/helper/mmaped_file.rb +150 -0
- data/lib/prometheus/client/mmaped_dict.rb +26 -66
- data/lib/prometheus/client/mmaped_value.rb +5 -3
- data/lib/prometheus/client/simple_value.rb +1 -1
- data/lib/prometheus/client/summary.rb +1 -1
- data/lib/prometheus/client/support/unicorn.rb +6 -3
- data/lib/prometheus/client/uses_value_type.rb +1 -1
- data/lib/prometheus/client/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84f31b1815d567cd2d00583255372973f739fad7
|
4
|
+
data.tar.gz: b2752140fb46a42db2081099a42943d33dc890d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b2e8c8b8ec3ea087da3d13e142c24622b56c007aa8679dc18beb4bd1ae66a28ef72344bcfa531c39e2c5778d83e3ba563b56cdcd49dfb811b4e6b34ace2f99d
|
7
|
+
data.tar.gz: 71488cc0a7a41f9c0cc230c11305d654f813b94502d440dd37ee98e79cf43feba70959ee9985a172b8778226de8035a78dac29d45e19f6a220b8a93ad33fe866
|
@@ -96,6 +96,7 @@ module Prometheus
|
|
96
96
|
metrics = {}
|
97
97
|
Dir.glob(File.join(path, '*.db')).sort.each do |f|
|
98
98
|
parts = File.basename(f, '.db').split('_')
|
99
|
+
parts[-1].gsub!(/(?<=.)-\d+/, '') # remove trailing file number
|
99
100
|
type = parts[0].to_sym
|
100
101
|
|
101
102
|
MmapedDict.read_all_values(f).each do |key, value|
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'prometheus/client'
|
2
|
+
require 'mmap'
|
3
|
+
|
4
|
+
module Prometheus
|
5
|
+
module Client
|
6
|
+
module Helper
|
7
|
+
class MmapedFile < Mmap
|
8
|
+
MINIMUM_SIZE = 8
|
9
|
+
attr_reader :filepath, :size
|
10
|
+
|
11
|
+
def initialize(filepath, mode = 'r', protection = Mmap::MAP_SHARED, options = {})
|
12
|
+
@filepath = filepath
|
13
|
+
|
14
|
+
File.open(filepath, 'a+b') do |file|
|
15
|
+
file.truncate(initial_mmap_file_size) if file.size < MINIMUM_SIZE
|
16
|
+
@size = file.size
|
17
|
+
end
|
18
|
+
|
19
|
+
super(filepath, mode, protection, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def pid
|
23
|
+
self[0..3].unpack('l')[0]
|
24
|
+
end
|
25
|
+
|
26
|
+
def pid=(value)
|
27
|
+
self[0..3] = [value].pack('l')
|
28
|
+
end
|
29
|
+
|
30
|
+
def used
|
31
|
+
self[4..7].unpack('l')[0]
|
32
|
+
end
|
33
|
+
|
34
|
+
def used=(value)
|
35
|
+
self[4..7] = [value].pack('l')
|
36
|
+
end
|
37
|
+
|
38
|
+
def locked_to_process?
|
39
|
+
pid > 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def lock_owner?
|
43
|
+
pid == Process.pid
|
44
|
+
end
|
45
|
+
|
46
|
+
def lock_to_process
|
47
|
+
return true if lock_owner?
|
48
|
+
|
49
|
+
if locked_to_process?
|
50
|
+
false
|
51
|
+
else
|
52
|
+
self.pid = Process.pid
|
53
|
+
|
54
|
+
# check if PID was correctly written
|
55
|
+
lock_owner?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def unlock
|
60
|
+
return unless lock_owner?
|
61
|
+
|
62
|
+
self.pid = 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def initial_mmap_file_size
|
66
|
+
Prometheus::Client.configuration.initial_mmap_file_size
|
67
|
+
end
|
68
|
+
|
69
|
+
def entries
|
70
|
+
return Enumerator.new {} if used.zero?
|
71
|
+
|
72
|
+
Enumerator.new do |yielder|
|
73
|
+
used_ = used # cache used to avoid unnecessary unpack operations
|
74
|
+
|
75
|
+
pos = 12 # pid + used + padding offset
|
76
|
+
while pos < used_
|
77
|
+
data = slice(pos..-1)
|
78
|
+
encoded_len, = data.unpack('l')
|
79
|
+
padding_len = 8 - (encoded_len + 4) % 8
|
80
|
+
value_offset = 4 + encoded_len + padding_len
|
81
|
+
|
82
|
+
yielder.yield data, encoded_len, value_offset
|
83
|
+
|
84
|
+
pos += value_offset + 8
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_entry(data, value)
|
90
|
+
self.used = 12 if used.zero?
|
91
|
+
|
92
|
+
# Pad to be 8-byte aligned.
|
93
|
+
padded = data + (' ' * (8 - (data.length + 4) % 8))
|
94
|
+
entry = [data.length, padded, value].pack("lA#{padded.length}d")
|
95
|
+
used_ = used
|
96
|
+
|
97
|
+
while (used_ + entry.length) > @size do
|
98
|
+
extend(size)
|
99
|
+
@size = File.size(filepath)
|
100
|
+
end
|
101
|
+
|
102
|
+
self[used_..used_ + entry.length] = entry
|
103
|
+
|
104
|
+
self.used = used_ + entry.length
|
105
|
+
end
|
106
|
+
|
107
|
+
def close
|
108
|
+
munmap
|
109
|
+
end
|
110
|
+
|
111
|
+
class << self
|
112
|
+
def open_readonly(filepath)
|
113
|
+
MmapedFile.new(filepath, 'r', Mmap::MAP_PRIVATE)
|
114
|
+
end
|
115
|
+
|
116
|
+
def open(filepath)
|
117
|
+
MmapedFile.new(filepath, 'rw', Mmap::MAP_SHARED)
|
118
|
+
end
|
119
|
+
|
120
|
+
def lock_to_process(filepath)
|
121
|
+
m = open(filepath)
|
122
|
+
if m.lock_to_process
|
123
|
+
Kernel.at_exit do
|
124
|
+
open(filepath).unlock
|
125
|
+
end
|
126
|
+
end
|
127
|
+
ensure
|
128
|
+
m.close
|
129
|
+
end
|
130
|
+
|
131
|
+
def ensure_process_exclusive_file(file_prefix = 'mmaped_file')
|
132
|
+
path = nil
|
133
|
+
filename_number = 0
|
134
|
+
until path && Helper::MmapedFile.lock_to_process(path)
|
135
|
+
filename = "#{file_prefix}_#{Prometheus::Client.pid}-#{filename_number}.db"
|
136
|
+
path = File.join(Prometheus::Client.configuration.multiprocess_files_dir, filename)
|
137
|
+
filename_number += 1
|
138
|
+
end
|
139
|
+
path
|
140
|
+
end
|
141
|
+
|
142
|
+
def open_process_exclusive_file(file_prefix = 'mmaped_file')
|
143
|
+
filename = Helper::MmapedFile.ensure_process_exclusive_file(file_prefix)
|
144
|
+
open(filename)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
+
require 'prometheus/client/helper/mmaped_file'
|
1
2
|
require 'prometheus/client'
|
2
|
-
require 'mmap'
|
3
3
|
|
4
4
|
module Prometheus
|
5
5
|
module Client
|
@@ -13,36 +13,34 @@ module Prometheus
|
|
13
13
|
# There's then a number of entries, consisting of a 4 byte int which is the
|
14
14
|
# size of the next field, a utf-8 encoded string key, padding to an 8 byte
|
15
15
|
# alignment, and then a 8 byte float which is the value.
|
16
|
-
#
|
17
|
-
# TODO(julius): dealing with Mmap.new, truncate etc. errors?
|
18
16
|
class MmapedDict
|
19
|
-
MINIMUM_SIZE =
|
20
|
-
attr_reader :m, :
|
17
|
+
MINIMUM_SIZE = 8
|
18
|
+
attr_reader :m, :used, :positions
|
21
19
|
|
22
|
-
def initialize(
|
20
|
+
def initialize(m)
|
23
21
|
@mutex = Mutex.new
|
24
|
-
|
25
|
-
|
22
|
+
|
23
|
+
@m = m
|
24
|
+
# @m.mlock # TODO: Ensure memory is locked to RAM
|
25
|
+
|
26
|
+
@used = @m.used
|
27
|
+
|
28
|
+
@positions = {}
|
29
|
+
read_all_positions.each do |key, _, pos|
|
30
|
+
@positions[key] = pos
|
31
|
+
end
|
26
32
|
rescue StandardError => e
|
27
|
-
raise ParsingError, "exception #{e} while processing metrics file #{
|
33
|
+
raise ParsingError, "exception #{e} while processing metrics file #{path}"
|
28
34
|
end
|
29
35
|
|
30
36
|
# Yield (key, value). No locking is performed.
|
31
37
|
def self.read_all_values(f)
|
32
|
-
m =
|
33
|
-
used, = m[0..3].unpack('l')
|
34
|
-
pos = 8
|
35
|
-
values = []
|
36
|
-
while pos < used
|
37
|
-
data = m.slice(pos..-1)
|
38
|
-
encoded_len, = data.unpack('l')
|
39
|
-
value_offset = 4 + encoded_len + (8 - (encoded_len + 4) % 8)
|
38
|
+
m = Helper::MmapedFile.open(f)
|
40
39
|
|
40
|
+
m.entries.map do |data, encoded_len, value_offset|
|
41
41
|
encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
|
42
|
-
|
43
|
-
pos += value_offset + 8
|
42
|
+
[encoded, value]
|
44
43
|
end
|
45
|
-
values
|
46
44
|
ensure
|
47
45
|
m.munmap
|
48
46
|
end
|
@@ -66,15 +64,13 @@ module Prometheus
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def path
|
69
|
-
@
|
67
|
+
@m.filepath unless @m.nil?
|
70
68
|
end
|
71
69
|
|
72
70
|
def close
|
73
|
-
@m.
|
71
|
+
@m.close
|
74
72
|
rescue TypeError => e
|
75
73
|
Prometheus::Client.logger.warn("munmap raised error #{e}")
|
76
|
-
ensure
|
77
|
-
@f.close
|
78
74
|
end
|
79
75
|
|
80
76
|
def initial_mmap_file_size
|
@@ -83,58 +79,22 @@ module Prometheus
|
|
83
79
|
|
84
80
|
private
|
85
81
|
|
86
|
-
def process_file
|
87
|
-
@f.truncate(initial_mmap_file_size) if @f.size < MINIMUM_SIZE
|
88
|
-
|
89
|
-
@capacity = @f.size
|
90
|
-
@m = Mmap.new(@f.path, 'rw', Mmap::MAP_SHARED)
|
91
|
-
# @m.mlock # TODO: Why does this raise an error?
|
92
|
-
|
93
|
-
@positions = {}
|
94
|
-
@used = @m[0..3].unpack('l')[0]
|
95
|
-
if @used == 0
|
96
|
-
@used = 8
|
97
|
-
@m[0..3] = [@used].pack('l')
|
98
|
-
else
|
99
|
-
read_all_positions.each do |key, _, pos|
|
100
|
-
@positions[key] = pos
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
82
|
# Initialize a value. Lock must be held by caller.
|
106
83
|
def init_value(key)
|
107
|
-
|
108
|
-
padded = key + (' ' * (8 - (key.length + 4) % 8))
|
109
|
-
value = [key.length, padded, 0.0].pack("lA#{padded.length}d")
|
110
|
-
while @used + value.length > @capacity
|
111
|
-
@capacity *= 2
|
112
|
-
@f.truncate(@capacity)
|
113
|
-
@m.unmap
|
114
|
-
@m = Mmap.new(@f.path, 'rw', Mmap::MAP_SHARED)
|
115
|
-
end
|
116
|
-
@m[@used..@used + value.length] = value
|
84
|
+
@m.add_entry(key, 0.0)
|
117
85
|
|
118
86
|
# Update how much space we've used.
|
119
|
-
@used
|
120
|
-
|
87
|
+
@used = @m.used
|
88
|
+
|
121
89
|
@positions[key] = @used - 8
|
122
90
|
end
|
123
91
|
|
124
92
|
# Yield (key, value, pos). No locking is performed.
|
125
93
|
def read_all_positions
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
data = @m.slice(pos..-1)
|
130
|
-
encoded_len = data.unpack('l')[0]
|
131
|
-
padding_len = 8 - (encoded_len + 4) % 8
|
132
|
-
encoded = data.unpack(format('@4A%d', encoded_len))
|
133
|
-
pos += 4 + encoded_len + padding_len
|
134
|
-
values << [encoded, pos]
|
135
|
-
pos += 8
|
94
|
+
@m.entries.map do |data, encoded_len, value_offset|
|
95
|
+
encoded, = data.unpack(format('@4A%d', encoded_len))
|
96
|
+
[encoded, value_offset]
|
136
97
|
end
|
137
|
-
values
|
138
98
|
end
|
139
99
|
end
|
140
100
|
end
|
@@ -81,12 +81,11 @@ module Prometheus
|
|
81
81
|
@@files_lock.synchronize do
|
82
82
|
unless @@files.has_key?(@file_prefix)
|
83
83
|
unless @file.nil?
|
84
|
-
puts @file
|
85
84
|
@file.close
|
86
85
|
end
|
86
|
+
mmaped_file = Helper::MmapedFile.open_process_exclusive_file(@file_prefix)
|
87
87
|
|
88
|
-
|
89
|
-
@@files[@file_prefix] = MmapedDict.new(filename)
|
88
|
+
@@files[@file_prefix] = MmapedDict.new(mmaped_file)
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
@@ -110,12 +109,15 @@ module Prometheus
|
|
110
109
|
@file.write_value(key, val)
|
111
110
|
rescue StandardError => e
|
112
111
|
Prometheus::Client.logger.warn("writing value to #{@file.path} failed with #{e}")
|
112
|
+
Prometheus::Client.logger.debug(e.backtrace.join("\n"))
|
113
113
|
end
|
114
114
|
|
115
115
|
def read_value(key)
|
116
116
|
@file.read_value(key)
|
117
117
|
rescue StandardError => e
|
118
118
|
Prometheus::Client.logger.warn("reading value from #{@file.path} failed with #{e}")
|
119
|
+
Prometheus::Client.logger.debug(e.backtrace.join("\n"))
|
120
|
+
0
|
119
121
|
end
|
120
122
|
end
|
121
123
|
end
|
@@ -4,9 +4,12 @@ module Prometheus
|
|
4
4
|
module Unicorn
|
5
5
|
def self.worker_pid_provider
|
6
6
|
wid = worker_id
|
7
|
-
Process.pid if wid.nil?
|
8
|
-
|
9
|
-
|
7
|
+
wid = Process.pid if wid.nil?
|
8
|
+
if wid.nil?
|
9
|
+
"process_pid_#{Process.pid}"
|
10
|
+
else
|
11
|
+
"worker_id_#{wid}"
|
12
|
+
end
|
10
13
|
end
|
11
14
|
|
12
15
|
def self.worker_id
|
@@ -11,7 +11,7 @@ module Prometheus
|
|
11
11
|
def value_object(type, metric_name, name, labels, *args)
|
12
12
|
value_class.new(type, metric_name, name, labels, *args)
|
13
13
|
rescue StandardError => e
|
14
|
-
Prometheus::Client.logger.info("error #{e} while creating instance of #{value_class}
|
14
|
+
Prometheus::Client.logger.info("error #{e} while creating instance of #{value_class} defaulting to SimpleValue")
|
15
15
|
Prometheus::Client::SimpleValue.new(type, metric_name, name, labels)
|
16
16
|
end
|
17
17
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus-client-mmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.beta15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schmidt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mmap2
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/prometheus/client/formats/text.rb
|
46
46
|
- lib/prometheus/client/gauge.rb
|
47
47
|
- lib/prometheus/client/helper/json_parser.rb
|
48
|
+
- lib/prometheus/client/helper/mmaped_file.rb
|
48
49
|
- lib/prometheus/client/histogram.rb
|
49
50
|
- lib/prometheus/client/label_set_validator.rb
|
50
51
|
- lib/prometheus/client/metric.rb
|