prometheus-client-mmap 0.9.2 → 0.9.3

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
  SHA1:
3
- metadata.gz: d8752d354419c2916e63d9d02e2891093725d6d0
4
- data.tar.gz: 2abff88dd92d618f310ede7213e061b74922c164
3
+ metadata.gz: 5e154df3b59ab07dafccd92bdcb586be4e47d55d
4
+ data.tar.gz: 3aae7936fe1cc4ed0a3b9012a161e669547a2c0f
5
5
  SHA512:
6
- metadata.gz: 7e1f15bf7e89250e0b0fdff424a4e1fc1e06f35d333b0cb633d2b40bb53bd8d1b7cc94b8181d578369000593fe59319cb7db28f345654d0a5eee40314fd09811
7
- data.tar.gz: 9464dd49c6fd0d273ff1790c2524798dd54b5c2adc4ed911bd5901e2c5a7ce213e78a13d7dd024cac6a1afef3af46151c8903281d77112ec42877a58d36c01c5
6
+ metadata.gz: 2933cecd564d961fc08fb733f62bfd74b647ec4797f37ee9d72df9d9563373c5f639fa53687f8d99152a81bc0cf76c4b3a17b29941ba7fab25aad3df8cb5bc71
7
+ data.tar.gz: 38f8d56e76388d059961af5a31bbe421243c2d4ffd18d2ba25e6102ba8a3c5c359b4dd948269f230420713325334762b27cb876e0ac861fada331e56e7f3cc49
@@ -66,7 +66,7 @@ module Prometheus
66
66
  value_offset = entry_len + padding_len # align to 8 bytes
67
67
  pos += value_offset
68
68
 
69
- if value_offset > 0 && (pos + VALUE_BYTES) < size # if positions are safe
69
+ if value_offset > 0 && (pos + VALUE_BYTES) <= size # if positions are safe
70
70
  yielder.yield data, encoded_len, value_offset, pos
71
71
  else
72
72
  raise ParsingError, "data slice is nil at pos #{pos}" unless ignore_errors
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.9.2'.freeze
3
+ VERSION = '0.9.3'.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.9.2
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schmidt
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-05-09 00:00:00.000000000 Z
12
+ date: 2018-05-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fuzzbert
@@ -83,8 +83,6 @@ extensions:
83
83
  extra_rdoc_files: []
84
84
  files:
85
85
  - README.md
86
- - ext/fast_mmaped_file/#file_parsing.c#
87
- - ext/fast_mmaped_file/#value_access.c#
88
86
  - ext/fast_mmaped_file/extconf.rb
89
87
  - ext/fast_mmaped_file/fast_mmaped_file.c
90
88
  - ext/fast_mmaped_file/file_format.c
@@ -104,13 +102,10 @@ files:
104
102
  - ext/fast_mmaped_file/value_access.h
105
103
  - lib/prometheus.rb
106
104
  - lib/prometheus/client.rb
107
- - lib/prometheus/client/#histogram.rb#
108
- - lib/prometheus/client/#mmaped_dict.rb#
109
105
  - lib/prometheus/client/configuration.rb
110
106
  - lib/prometheus/client/counter.rb
111
107
  - lib/prometheus/client/formats/text.rb
112
108
  - lib/prometheus/client/gauge.rb
113
- - lib/prometheus/client/helper/#entry_parser.rb#
114
109
  - lib/prometheus/client/helper/entry_parser.rb
115
110
  - lib/prometheus/client/helper/file_locker.rb
116
111
  - lib/prometheus/client/helper/json_parser.rb
@@ -1,216 +0,0 @@
1
- #include <hashmap.h>
2
- #include <jsmn.h>
3
- #include <ruby.h>
4
-
5
- #include "file_format.h"
6
- #include "file_parsing.h"
7
- #include "globals.h"
8
- #include "utils.h"
9
-
10
- HASHMAP_FUNCS_CREATE(entry, const entry_t, entry_t)
11
-
12
- typedef int (*compare_fn)(const void *a, const void *b);
13
-
14
- static size_t hashmap_hash_entry(const entry_t *entry) {
15
- size_t hash = 0;
16
-
17
- for (size_t i = 0; i < entry->json_size; i++) {
18
- hash += *(const char *)(entry->json + i);
19
- hash += (hash << 10);
20
- hash ^= (hash >> 6);
21
- }
22
- hash += (hash << 3);
23
- hash ^= (hash >> 11);
24
- hash += (hash << 15);
25
- return hash;
26
- }
27
-
28
- static int hashmap_compare_entry(const entry_t *a, const entry_t *b) {
29
- if (a->json_size != b->json_size) {
30
- return -1;
31
- }
32
-
33
- if (is_pid_significant(a) && (rb_str_equal(a->pid, b->pid) == Qfalse)) {
34
- return -1;
35
- }
36
-
37
- return strncmp(a->json, b->json, a->json_size);
38
- }
39
-
40
- static entry_t *copy_entry(const entry_t *entry) {
41
- entry_t *copied = (entry_t *)malloc(sizeof(entry_t));
42
- if (copied == NULL) {
43
- return NULL;
44
- }
45
- memcpy(copied, entry, sizeof(entry_t));
46
-
47
- copied->json = malloc(entry->json_size);
48
- if (copied->json == NULL) {
49
- free(copied);
50
- return NULL;
51
- }
52
-
53
- memcpy(copied->json, entry->json, entry->json_size);
54
-
55
- return copied;
56
- }
57
-
58
- static void entry_free(entry_t *entry) {
59
- free(entry->json);
60
- free(entry);
61
- }
62
-
63
- static void merge_entry(entry_t *found, const entry_t *entry) {
64
- if (entry->type == sym_gauge) {
65
- if (entry->multiprocess_mode == sym_min) {
66
- found->value = min(found->value, entry->value);
67
- } else if (entry->multiprocess_mode == sym_max) {
68
- found->value = max(found->value, entry->value);
69
- } else if (entry->multiprocess_mode == sym_livesum) {
70
- found->value += entry->value;
71
- } else {
72
- found->value = entry->value;
73
- }
74
- } else {
75
- found->value += entry->value;
76
- }
77
- }
78
-
79
- static int process_entry(struct hashmap *map, const entry_t *entry) {
80
- entry_t *found = entry_hashmap_get(map, entry);
81
- if (found) {
82
- merge_entry(found, entry);
83
- } else {
84
- entry_t *copy = copy_entry(entry);
85
- if (copy == NULL) {
86
- save_exception(rb_eNoMemError, "Failed copying metrics entry");
87
- return 0;
88
- }
89
- entry_hashmap_put(map, copy, copy); // use the hashmap like hashset actually
90
- }
91
- return 1;
92
- }
93
-
94
- inline entry_t entry_new(buffer_t *source, uint32_t pos, uint32_t encoded_len, file_t *file_info) {
95
- entry_t entry = (entry_t){.json = source->buffer + pos,
96
- .json_size = encoded_len,
97
- .pid = file_info->pid,
98
- .multiprocess_mode = file_info->multiprocess_mode,
99
- .type = file_info->type,
100
- .name_len = 0};
101
-
102
- uint32_t value_offset = encoded_len + padding_length(encoded_len);
103
- memcpy(&entry.value, entry.json + value_offset, sizeof(double));
104
-
105
- return entry;
106
- }
107
-
108
- static int add_parsed_name(entry_t *entry) {
109
- jsmn_parser parser;
110
- jsmn_init(&parser);
111
-
112
- jsmntok_t t[2] = {(jsmntok_t){}, (jsmntok_t){}};
113
- jsmn_parse(&parser, entry->json, entry->json_size, t, 2);
114
-
115
- jsmntok_t *name_tok = &t[1];
116
-
117
- if (name_tok->start < name_tok->end && name_tok->start > 0) {
118
- entry->name = entry->json + name_tok->start;
119
- entry->name_len = name_tok->end - name_tok->start;
120
- return 1;
121
- }
122
- return 0;
123
- }
124
-
125
- static int entry_lexical_comparator(const entry_t **a, const entry_t **b) {
126
- size_t min_length = min((*a)->json_size, (*b)->json_size);
127
- return strncmp((*a)->json, (*b)->json, min_length);
128
- }
129
-
130
- void hashmap_setup(struct hashmap *map) {
131
- hashmap_init(map, (size_t(*)(const void *))hashmap_hash_entry,
132
- (int (*)(const void *, const void *))hashmap_compare_entry, 1000);
133
- }
134
-
135
- void entries_destroy(struct hashmap *map) {
136
- struct hashmap_iter *iter;
137
- for (iter = hashmap_iter(map); iter; iter = hashmap_iter_next(map, iter)) {
138
- entry_t *entry = (entry_t *)entry_hashmap_iter_get_key(iter);
139
- entry_free(entry);
140
- }
141
- }
142
-
143
- int process_buffer(file_t *file_info, buffer_t *source, struct hashmap *map) {
144
- if (source->size < START_POSITION) {
145
- // nothing to read
146
- return 1;[
147
- }
148
- uint32_t used;
149
- memcpy(&used, source->buffer, sizeof(uint32_t));
150
-
151
- if (used > source->size) {
152
- save_exception(prom_eParsingError, "source file %s corrupted, used %u > file size %u", file_info->path, used,
153
- source->size);
154
- return 0;
155
- }
156
-
157
- uint32_t pos = START_POSITION;
158
- while (pos + sizeof(uint32_t) < used) {
159
- uint32_t encoded_len;
160
- memcpy(&encoded_len, source->buffer + pos, sizeof(uint32_t));
161
- pos += sizeof(uint32_t);
162
-
163
- uint32_t value_offset = encoded_len + padding_length(encoded_len);
164
-
165
- if (pos + value_offset + sizeof(double) > used) {
166
- save_exception(prom_eParsingError, "source file %s corrupted, used %u < stored data length %u",
167
- file_info->path, used, pos + value_offset + sizeof(double));
168
- return 0;
169
- }
170
- entry_t entry = entry_new(source, pos, encoded_len, file_info);
171
-
172
- if (!process_entry(map, &entry)) {
173
- entries_destroy(map);
174
- return 0;
175
- }
176
-
177
- pos += value_offset + sizeof(double);
178
- }
179
- return 1;
180
- }
181
-
182
- int sort_map_entries(const struct hashmap *map, entry_t ***sorted_entries) {
183
- size_t num = hashmap_size(map);
184
-
185
- size_t list_size = num * sizeof(entry_t *);
186
- entry_t **list = malloc(list_size);
187
-
188
- if (list == NULL) {
189
- save_exception(rb_eNoMemError, "Couldn't allocate %zu memory", list_size);
190
- return 0;
191
- }
192
-
193
- size_t cnt = 0;
194
- struct hashmap_iter *iter;
195
- for (iter = hashmap_iter(map); iter; iter = hashmap_iter_next(map, iter)) {
196
- entry_t *entry = (entry_t *)entry_hashmap_iter_get_key(iter);
197
- if (add_parsed_name(entry)) {
198
- list[cnt] = entry;
199
- cnt++;
200
- }
201
- }
202
- if (cnt != num) {
203
- save_exception(rb_eRuntimeError, "Processed entries %zu != map entries %zu", cnt, num);
204
- free(list);
205
- return 0;
206
- }
207
-
208
- qsort(list, cnt, sizeof(entry_t *), (compare_fn)&entry_lexical_comparator);
209
- *sorted_entries = list;
210
- return 1;
211
- }
212
-
213
- int is_pid_significant(const entry_t *e) {
214
- ID mp = e->multiprocess_mode;
215
- return e->type == sym_gauge && !(mp == sym_min || mp == sym_max || mp == sym_livesum);
216
- }
@@ -1,203 +0,0 @@
1
- #include <ruby.h>
2
- #include <ruby/intern.h>
3
-
4
- #include <errno.h>
5
- #include <fcntl.h>
6
- #include <sys/mman.h>
7
- #include <unistd.h>
8
-
9
- #include "file_format.h"
10
- #include "mmap.h"
11
- #include "value_access.h"
12
-
13
- static int open_and_extend_file(mm_ipc *i_mm, size_t len) {
14
- int fd;
15
-
16
- if ((fd = open(i_mm->t->path, i_mm->t->smode)) == -1) {
17
- rb_raise(rb_eArgError, "Can't open %s", i_mm->t->path);
18
- }
19
-
20
- if (lseek(fd, len - i_mm->t->len - 1, SEEK_END) == -1) {
21
- close(fd);
22
- rb_raise(rb_eIOError, "Can't lseek %zu", len - i_mm->t->len - 1);
23
- }
24
-
25
- if (write(fd, "\000", 1) != 1) {
26
- close(fd);
27
- rb_raise(rb_eIOError, "Can't extend %s", i_mm->t->path);
28
- }
29
-
30
- return fd;
31
- }
32
-
33
- static void expand(mm_ipc *i_mm, size_t len) {
34
- if (len < i_mm->t->len) {
35
- rb_raise(rb_eArgError, "Can't reduce the size of mmap");
36
- }
37
-
38
- if (munmap(i_mm->t->addr, i_mm->t->len)) {
39
- rb_raise(rb_eArgError, "munmap failed");
40
- }
41
-
42
- int fd = open_and_extend_file(i_mm, len);
43
-
44
- i_mm->t->addr = mmap(0, len, i_mm->t->pmode, i_mm->t->vscope, fd, i_mm->t->offset);
45
-
46
- if (i_mm->t->addr == MAP_FAILED) {
47
- close(fd);
48
- rb_raise(rb_eArgError, "mmap failed");
49
- }
50
-
51
- if (close(fd) == -1) {
52
- rb_raise(rb_eArgError, "Can't close %s", i_mm->t->path);
53
- }
54
-
55
- if ((i_mm->t->flag & MM_LOCK) && mlock(i_mm->t->addr, len) == -1) {
56
- rb_raise(rb_eArgError, "mlock(%d)", errno);
57
- }
58
- i_mm->t->len = len;
59
- i_mm->t->real = len;
60
- }
61
-
62
- static void save_entry(mm_ipc *i_mm, size_t offset, VALUE key, VALUE value) {
63
- uint32_t key_length = (uint32_t)RSTRING_LEN(key);
64
-
65
- char *pos = (char *)i_mm->t->addr + offset;
66
-
67
- memcpy(pos, &key_length, sizeof(uint32_t));
68
- pos += sizeof(uint32_t);
69
-
70
- memmove(pos, StringValuePtr(key), key_length);
71
- pos += key_length;
72
-
73
- memset(pos, ' ', padding_length(key_length)); // TODO: considder padding with /0
74
- pos += padding_length(key_length);
75
-
76
- double val = NUM2DBL(value);
77
- memcpy(pos, &val, sizeof(double));
78
- }
79
-
80
- static void save_value(mm_ipc *i_mm, VALUE _offset, VALUE value) {
81
- Check_Type(_offset, T_FIXNUM);
82
- size_t offset = NUM2UINT(_offset);
83
- if ((i_mm->t->real + sizeof(double)) <= offset) {
84
- rb_raise(rb_eIndexError, "offset %zu out of string", offset);
85
- }
86
-
87
- if (i_mm->t->flag & MM_FROZEN) {
88
- rb_error_frozen("mmap");
89
- }
90
-
91
- char *pos = (char *)i_mm->t->addr + offset;
92
-
93
- double val = NUM2DBL(value);
94
- memcpy(pos, &val, sizeof(double));
95
- }
96
-
97
- static VALUE load_value(mm_ipc *i_mm, VALUE _offset) {
98
- Check_Type(_offset, T_FIXNUM);
99
- size_t offset = NUM2UINT(_offset);
100
- if ((i_mm->t->real + sizeof(double)) <= offset) {
101
- rb_raise(rb_eIndexError, "offset %zu out of string", offset);
102
- }
103
-
104
- char *pos = (char *)i_mm->t->addr + offset;
105
-
106
- double value;
107
- memcpy(&value, pos, sizeof(double));
108
- return DBL2NUM(value);
109
- }
110
-
111
- inline uint32_t load_used(mm_ipc *i_mm) {
112
- uint32_t used = *((uint32_t *)i_mm->t->addr);
113
-
114
- if (used == 0) {
115
- used = START_POSITION;
116
- }
117
- return used;
118
- }
119
-
120
- inline void save_used(mm_ipc *i_mm, uint32_t used) { *((uint32_t *)i_mm->t->addr) = used; }
121
-
122
- static VALUE initialize_entry(mm_ipc *i_mm, VALUE positions, VALUE key, VALUE value) {
123
- if (i_mm->t->flag & MM_FROZEN) {
124
- rb_error_frozen("mmap");
125
- }
126
-
127
- if (RSTRING_LEN(key) > INT32_MAX) {
128
- rb_raise(rb_eArgError, "string length gt %d", INT32_MAX);
129
- }
130
-
131
- uint32_t key_length = (uint32_t)RSTRING_LEN(key);
132
- uint32_t value_offset = sizeof(uint32_t) + key_length + padding_length(key_length);
133
- uint32_t entry_length = value_offset + sizeof(double);
134
-
135
- uint32_t used = load_used(i_mm);
136
- while (i_mm->t->len < (used + entry_length)) {
137
- expand(i_mm, i_mm->t->len * 2);
138
- }
139
- save_entry(i_mm, used, key, value);
140
- save_used(i_mm, used + entry_length);
141
-
142
- return rb_hash_aset(positions, key, INT2NUM(used + value_offset));
143
- }
144
-
145
- VALUE method_fetch_entry(VALUE self, VALUE positions, VALUE key, VALUE default_value) {
146
- Check_Type(positions, T_HASH);
147
- Check_Type(key, T_STRING);
148
-
149
- mm_ipc *i_mm;
150
- GET_MMAP(self, i_mm, MM_MODIFY);
151
-
152
- VALUE position = rb_hash_lookup(positions, key);
153
-
154
- if (position != Qnil) {
155
- return load_value(i_mm, position);
156
- }
157
-
158
- position = initialize_entry(i_mm, positions, key, default_value);
159
- return load_value(i_mm, position);
160
- }
161
-
162
- VALUE method_upsert_entry(VALUE self, VALUE positions, VALUE key, VALUE value) {
163
- Check_Type(positions, T_HASH);
164
- Check_Type(key, T_STRING);
165
-
166
- mm_ipc *i_mm;
167
- GET_MMAP(self, i_mm, MM_MODIFY);
168
-
169
- VALUE position = rb_hash_lookup(positions, key);
170
-
171
- if (position != Qnil) {
172
- save_value(i_mm, position, value);
173
- return load_value(i_mm, position);
174
- }
175
-
176
- position = initialize_entry(i_mm, positions, key, value);
177
- return load_value(i_mm, position);
178
- }
179
-
180
- VALUE method_load_used(VALUE self) {
181
- mm_ipc *i_mm;
182
-
183
- GET_MMAP(self, i_mm, MM_MODIFY);
184
- return UINT2NUM(load_used(i_mm));
185
- }
186
-
187
- VALUE method_save_used(VALUE self, VALUE value) {
188
- Check_Type(value, T_FIXNUM);
189
- mm_ipc *i_mm;
190
-
191
- GET_MMAP(self, i_mm, MM_MODIFY);
192
-
193
- if (i_mm->t->flag & MM_FROZEN) {
194
- rb_error_frozen("mmap");
195
- }
196
-
197
- if (i_mm->t->len < INITIAL_SIZE) {
198
- expand(i_mm, INITIAL_SIZE);
199
- }
200
-
201
- save_used(i_mm, NUM2UINT(value));
202
- return value;
203
- }
@@ -1,79 +0,0 @@
1
- ip route get 1require '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
14
-
15
- def initialize(type, name, labels, buckets)
16
- @sum = value_object(type, name, "#{name}_sum", labels)
17
- # TODO: get rid of total and use +Inf bucket instead.
18
- @total = value_object(type, name, "#{name}_count", labels)
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
-
29
- each_key do |bucket|
30
- self[bucket].increment() if value <= bucket
31
- end
32
- end
33
-
34
- def get()
35
- hash = {}
36
- each_key do |bucket|
37
- hash[bucket] = self[bucket].get()
38
- end
39
- hash
40
- end
41
- end
42
-
43
- # DEFAULT_BUCKETS are the default Histogram buckets. The default buckets
44
- # are tailored to broadly measure the response time (in seconds) of a
45
- # network service. (From DefBuckets client_golang)
46
- DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
47
- 2.5, 5, 10].freeze
48
-
49
- # Offer a way to manually specify buckets
50
- def initialize(name, docstring, base_labels = {},
51
- buckets = DEFAULT_BUCKETS)
52
- raise ArgumentError, 'Unsorted buckets, typo?' unless sorted? buckets
53
-
54
- @buckets = buckets
55
- super(name, docstring, base_labels)
56
- end
57
-
58
- def type
59
- :histogram
60
- end
61
-
62
- def observe(labels, value)
63
- label_set = label_set_for(labels)
64
- synchronize { @values[label_set].observe(value) }
65
- end
66
-
67
- private
68
-
69
- def default(labels)
70
- # TODO: default function needs to know key of hash info (label names and values)
71
- Value.new(type, @name, labels, @buckets)
72
- end
73
-
74
- def sorted?(bucket)
75
- bucket.each_cons(2).all? { |i, j| i <= j }
76
- end
77
- end
78
- end
79
- end
@@ -1,108 +0,0 @@
1
- require 'prometheus/client/helper/mmaped_file'
2
- require 'prometheus/client'
3
-
4
- module Prometheus
5
- module Client
6
- class ParsingError < StandardError
7
- end
8
-
9
- # A dict of doubles, backed by an mmapped file.
10
- #
11
- # The file starts with a 4 byte int, indicating how much of it is used.
12
- # Then 4 bytes of padding.
13
- # There's then a number of entries, consisting of a 4 byte int which is the
14
- # size of the next field, a utf-8 encoded string key, padding to an 8 byte
15
- # alignment, and then a 8 byte float which is the value.
16
- #
17
- # For example, for :
18
- #
19
- # 0: | PID (4 bytes) | Used bytes (4 bytes) |
20
- # 1: | Padding (4 bytes) | Size of field (4 bytes) |
21
- # 2:
22
- # 2: | Entry 1 key (N bytes) | Padding (4 bytes) |
23
- # 3: | Entry 1 value (8 bytes) |
24
- # 4: | Size of field 2 | Padding (4 bytes) |
25
- # 5: | Entry 2 key (4 bytes) | Padding (4 byte) |
26
- # 6: | Entry 2 value (8 bytes) |
27
- class MmapedDict
28
- MINIMUM_SIZE = 8
29
- attr_reader :m, :used, :positions
30
-
31
- def initialize(m)
32
- @mutex = Mutex.new
33
-
34
- @m = m
35
- # @m.mlock # TODO: Ensure memory is locked to RAM
36
-
37
- @used = @m.used
38
-
39
- @positions = {}
40
- read_all_positions.each do |key, pos|
41
- @positions[key] = pos
42
- end
43
- rescue StandardError => e
44
- raise ParsingError, "exception #{e} while processing metrics file #{path}"
45
- end
46
-
47
- # Yield (key, value). No locking is performed.
48
- def self.read_all_values(f)
49
- m = Helper::MmapedFile.open(f)
50
-
51
- m.entries.map do |data, encoded_len, value_offset, _|
52
- encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
53
- [encoded, value]
54
- end
55
- ensure
56
- m.munmap
57
- end
58
-
59
- def read_value(key)
60
- @mutex.synchronize do
61
- init_value(key) unless @positions.key?(key)
62
- end
63
- pos = @positions[key]
64
- # We assume that reading from an 8 byte aligned value is atomic.
65
- @m[pos..pos + 7].unpack('d')[0]
66
- end
67
-
68
- def write_value(key, value)
69
- @mutex.synchronize do
70
- init_value(key) unless @positions.key?(key)
71
- end
72
- pos = @positions[key]
73
- # We assume that writing to an 8 byte aligned value is atomic.
74
- @m[pos..pos + 7] = [value].pack('d')
75
- end
76
-
77
- def path
78
- @m.filepath unless @m.nil?
79
- end
80
-
81
- def close
82
- @m.close
83
- rescue TypeError => e
84
- Prometheus::Client.logger.warn("munmap raised error #{e}")
85
- end
86
-
87
- private
88
-
89
- # Initialize a value. Lock must be held by caller.
90
- def init_value(key)
91
- @m.add_entry(key, 0.0)
92
-
93
- # Update how much space we've used.
94
- @used = @m.used
95
-
96
- @positions[key] = @used - 8
97
- end
98
-
99
- # Yield (key, pos). No locking is performed.
100
- def read_all_positions
101
- @m.entries.map do |data, encoded_len, _, absolute_pos|
102
- encoded, = data.unpack(format('@4A%d', encoded_len))
103
- [encoded, absolute_pos]
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,132 +0,0 @@
1
- require 'prometheus/client/helper/json_parser'
2
-
3
- module Prometheus
4
- module Client
5
- module Helper
6
- module EntryParser
7
- class ParsingError < RuntimeError;
8
- end
9
-
10
- MINIMUM_SIZE = 8
11
- START_POSITION = 8
12
- VALUE_BYTES = 8
13
- ENCODED_LENGTH_BYTES = 4
14
-
15
- def used
16
- slice(0..3).unpack('l')[0]
17
- end
18
-
19
- def parts
20
- @parts ||= File.basename(filepath, '.db')
21
- .split('_')
22
- .map { |e| e.gsub(/-\d+$/, '') } # remove trailing -number
23
- end
24
-
25
- def type
26
- parts[0].to_sym
27
- end
28
-
29
- def pid
30
- parts[2..-1].join('_')
31
- end
32
-
33
- def multiprocess_mode
34
- parts[1]
35
- end
36
-
37
- def empty?
38
- size < MINIMUM_SIZE || used.zero?
39
- end
40
-
41
- def entries(ignore_errors = false)
42
- return Enumerator.new {} if empty?
43
-
44
- Enumerator.new do |yielder|
45
- used_ = used # cache used to avoid unnecessary unpack operations
46
-
47
- pos = START_POSITION # used + padding offset
48
- while pos < used_ && pos < size && pos > 0
49
- data = slice(pos..-1)
50
- unless data
51
- raise ParsingError, "data slice is nil at pos #{pos}" unless ignore_errors
52
- pos += 8c
53
- next
54
- end
55
-
56
- encoded_len, first_encoded_bytes = data.unpack('LL')
57
- if encoded_len.nil? || encoded_len.zero? || first_encoded_bytes.nil? || first_encoded_bytes.zero?
58
- # do not parse empty data
59
- pos += 8
60
- next
61
- end
62
-
63
- entry_len = ENCODED_LENGTH_BYTES + encoded_len
64
- padding_len = 8 - entry_len % 8
65
-
66
- value_offset = entry_len + padding_len # align to 8 bytes
67
- pos += value_offset
68
-
69
- if value_offset > 0 && (pos + VALUE_BYTES) < size # if positions are safe
70
- yielder.yield data, encoded_len, value_offset, pos
71
- else
72
- raise ParsingError, "data slice is nil at pos #{pos}" unless ignore_errors
73
- end
74
- pos += VALUE_BYTES
75
- end
76
- end
77
- end
78
-
79
- def parsed_entries(ignore_errors = false)
80
- result = entries(ignore_errors).map do |data, encoded_len, value_offset, _|
81
- begin
82
- encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
83
- [encoded, value]
84
- rescue ArgumentError => e
85
- Prometheus::Client.logger.debug("Error processing data: #{bin_to_hex(data[0, 7])} len: #{encoded_len} value_offset: #{value_offset}")
86
- raise ParsingError, e unless ignore_errors
87
- end
88
- end
89
-
90
- metrics.reject!(&:nil?) if ignore_errors
91
- result
92
- end
93
-
94
- def to_metrics(metrics = {}, ignore_errors = false)
95
- parsed_entries(ignore_errors).each do |key, value|
96
- begin
97
- metric_name, name, labelnames, labelvalues = JsonParser.load(key)
98
- labelnames ||= []
99
- labelvalues ||= []
100
-
101
- metric = metrics.fetch(metric_name,
102
- metric_name: metric_name,
103
- help: 'Multiprocess metric',
104
- type: type,
105
- samples: [])
106
- if type == :gauge
107
- metric[:multiprocess_mode] = multiprocess_mode
108
- metric[:samples] += [[name, labelnames.zip(labelvalues) + [['pid', pid]], value]]
109
- else
110
- # The duplicates and labels are fixed in the next for.
111
- metric[:samples] += [[name, labelnames.zip(labelvalues), value]]
112
- end
113
- metrics[metric_name] = metric
114
-
115
- rescue JSON::ParserError => e
116
- raise ParsingError(e) unless ignore_errors
117
- end
118
- end
119
-
120
- metrics.reject!(&:nil?) if ignore_errors
121
- metrics
122
- end
123
-
124
- private
125
-
126
- def bin_to_hex(s)
127
- s.each_byte.map { |b| b.to_s(16) }.join
128
- end
129
- end
130
- end
131
- end
132
- end