prometheus-client-mmap 0.17.0 → 0.18.0

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
  SHA256:
3
- metadata.gz: 98e93b1f6cdd34ff4621cc7401fa1b02b6e81b069313899aabd28d1a1650cf7d
4
- data.tar.gz: d0ceb3c1535fa17e609baef147217593366bd3e0351254d231848ec18653c06d
3
+ metadata.gz: 28745ccfcec4f7f79be0f0d64258aecf66e19e35aaf28681bf7284833fe7adfc
4
+ data.tar.gz: 04dd8b1fa4c8f769daff34345dbd039940af4fdf3a72d9c948893ea5e1b56835
5
5
  SHA512:
6
- metadata.gz: 7cc06bff586223b764f098d18c213e59f072a3ab906c7f4c2e844bcc419c9b1037d39b564d2a441a8fac50fc913107f2c5864f06fe37b15e54eb1c828cf8f118
7
- data.tar.gz: 72c42e4bb89b7b0bfff4f5855ca3b42eff07c5dc1b8317940739fa0304e79eafca131c9909a80588d6ce1362313175c8a21bacb46b2ed2066114a49f8f3fdb31
6
+ metadata.gz: 75188a0202b23b8845a4fd13e315ec0d98eab57034d32528292e886e684208a681579a14f199c9a40eed70fe62452dc83faf661941247cddeed5e9c89b826add
7
+ data.tar.gz: 7842632f104eadbda9a1c1733d3384aa9743dfda25c20cf98f1d0f59201900b9b3143414bfcf333e9f61c0b18e2fbe1de46be044e7b719dbe2529d57a92d1e72
data/README.md CHANGED
@@ -68,29 +68,41 @@ integrated [example application](examples/rack/README.md).
68
68
  The Ruby client can also be used to push its collected metrics to a
69
69
  [Pushgateway][8]. This comes in handy with batch jobs or in other scenarios
70
70
  where it's not possible or feasible to let a Prometheus server scrape a Ruby
71
- process.
71
+ process. TLS and HTTP basic authentication are supported.
72
72
 
73
73
  ```ruby
74
74
  require 'prometheus/client'
75
75
  require 'prometheus/client/push'
76
76
 
77
- prometheus = Prometheus::Client.registry
77
+ registry = Prometheus::Client.registry
78
78
  # ... register some metrics, set/increment/observe/etc. their values
79
79
 
80
80
  # push the registry state to the default gateway
81
- Prometheus::Client::Push.new('my-batch-job').add(prometheus)
81
+ Prometheus::Client::Push.new(job: 'my-batch-job').add(registry)
82
82
 
83
- # optional: specify the instance name (instead of IP) and gateway
83
+ # optional: specify a grouping key that uniquely identifies a job instance, and gateway.
84
+ #
85
+ # Note: the labels you use in the grouping key must not conflict with labels set on the
86
+ # metrics being pushed. If they do, an error will be raised.
84
87
  Prometheus::Client::Push.new(
85
- 'my-job', 'instance-name', 'http://example.domain:1234').add(prometheus)
88
+ job: 'my-batch-job',
89
+ gateway: 'https://example.domain:1234',
90
+ grouping_key: { instance: 'some-instance', extra_key: 'foobar' }
91
+ ).add(registry)
86
92
 
87
- # If you want to replace any previously pushed metrics for a given instance,
93
+ # If you want to replace any previously pushed metrics for a given grouping key,
88
94
  # use the #replace method.
89
- Prometheus::Client::Push.new('my-batch-job', 'instance').replace(prometheus)
90
-
91
- # If you want to delete all previously pushed metrics for a given instance,
95
+ #
96
+ # Unlike #add, this will completely replace the metrics under the specified grouping key
97
+ # (i.e. anything currently present in the pushgateway for the specified grouping key, but
98
+ # not present in the registry for that grouping key will be removed).
99
+ #
100
+ # See https://github.com/prometheus/pushgateway#put-method for a full explanation.
101
+ Prometheus::Client::Push.new(job: 'my-batch-job').replace(registry)
102
+
103
+ # If you want to delete all previously pushed metrics for a given grouping key,
92
104
  # use the #delete method.
93
- Prometheus::Client::Push.new('my-batch-job', 'instance').delete
105
+ Prometheus::Client::Push.new(job: 'my-batch-job').delete
94
106
  ```
95
107
 
96
108
  ## Metrics
@@ -0,0 +1,692 @@
1
+ /*
2
+ * Copyright (c) 2016-2017 David Leeds <davidesleeds@gmail.com>
3
+ *
4
+ * Hashmap is free software; you can redistribute it and/or modify
5
+ * it under the terms of the MIT license. See LICENSE for details.
6
+ */
7
+
8
+ #include <stdlib.h>
9
+ #include <stdint.h>
10
+ #include <stdbool.h>
11
+ #include <string.h>
12
+ #include <errno.h>
13
+
14
+ #include "hashmap.h"
15
+
16
+ #ifndef HASHMAP_NOASSERT
17
+ #include <assert.h>
18
+ #define HASHMAP_ASSERT(expr) assert(expr)
19
+ #else
20
+ #define HASHMAP_ASSERT(expr)
21
+ #endif
22
+
23
+ /* Table sizes must be powers of 2 */
24
+ #define HASHMAP_SIZE_MIN (1 << 5) /* 32 */
25
+ #define HASHMAP_SIZE_DEFAULT (1 << 8) /* 256 */
26
+ #define HASHMAP_SIZE_MOD(map, val) ((val) & ((map)->table_size - 1))
27
+
28
+ /* Limit for probing is 1/2 of table_size */
29
+ #define HASHMAP_PROBE_LEN(map) ((map)->table_size >> 1)
30
+ /* Return the next linear probe index */
31
+ #define HASHMAP_PROBE_NEXT(map, index) HASHMAP_SIZE_MOD(map, (index) + 1)
32
+
33
+ /* Check if index b is less than or equal to index a */
34
+ #define HASHMAP_INDEX_LE(map, a, b) \
35
+ ((a) == (b) || (((b) - (a)) & ((map)->table_size >> 1)) != 0)
36
+
37
+
38
+ struct hashmap_entry {
39
+ void *key;
40
+ void *data;
41
+ #ifdef HASHMAP_METRICS
42
+ size_t num_collisions;
43
+ #endif
44
+ };
45
+
46
+
47
+ /*
48
+ * Enforce a maximum 0.75 load factor.
49
+ */
50
+ static inline size_t hashmap_table_min_size_calc(size_t num_entries)
51
+ {
52
+ return num_entries + (num_entries / 3);
53
+ }
54
+
55
+ /*
56
+ * Calculate the optimal table size, given the specified max number
57
+ * of elements.
58
+ */
59
+ static size_t hashmap_table_size_calc(size_t num_entries)
60
+ {
61
+ size_t table_size;
62
+ size_t min_size;
63
+
64
+ table_size = hashmap_table_min_size_calc(num_entries);
65
+
66
+ /* Table size is always a power of 2 */
67
+ min_size = HASHMAP_SIZE_MIN;
68
+ while (min_size < table_size) {
69
+ min_size <<= 1;
70
+ }
71
+ return min_size;
72
+ }
73
+
74
+ /*
75
+ * Get a valid hash table index from a key.
76
+ */
77
+ static inline size_t hashmap_calc_index(const struct hashmap *map,
78
+ const void *key)
79
+ {
80
+ return HASHMAP_SIZE_MOD(map, map->hash(key));
81
+ }
82
+
83
+ /*
84
+ * Return the next populated entry, starting with the specified one.
85
+ * Returns NULL if there are no more valid entries.
86
+ */
87
+ static struct hashmap_entry *hashmap_entry_get_populated(
88
+ const struct hashmap *map, struct hashmap_entry *entry)
89
+ {
90
+ for (; entry < &map->table[map->table_size]; ++entry) {
91
+ if (entry->key) {
92
+ return entry;
93
+ }
94
+ }
95
+ return NULL;
96
+ }
97
+
98
+ /*
99
+ * Find the hashmap entry with the specified key, or an empty slot.
100
+ * Returns NULL if the entire table has been searched without finding a match.
101
+ */
102
+ static struct hashmap_entry *hashmap_entry_find(const struct hashmap *map,
103
+ const void *key, bool find_empty)
104
+ {
105
+ size_t i;
106
+ size_t index;
107
+ size_t probe_len = HASHMAP_PROBE_LEN(map);
108
+ struct hashmap_entry *entry;
109
+
110
+ index = hashmap_calc_index(map, key);
111
+
112
+ /* Linear probing */
113
+ for (i = 0; i < probe_len; ++i) {
114
+ entry = &map->table[index];
115
+ if (!entry->key) {
116
+ if (find_empty) {
117
+ #ifdef HASHMAP_METRICS
118
+ entry->num_collisions = i;
119
+ #endif
120
+ return entry;
121
+ }
122
+ return NULL;
123
+ }
124
+ if (map->key_compare(key, entry->key) == 0) {
125
+ return entry;
126
+ }
127
+ index = HASHMAP_PROBE_NEXT(map, index);
128
+ }
129
+ return NULL;
130
+ }
131
+
132
+ /*
133
+ * Removes the specified entry and processes the proceeding entries to reduce
134
+ * the load factor and keep the chain continuous. This is a required
135
+ * step for hash maps using linear probing.
136
+ */
137
+ static void hashmap_entry_remove(struct hashmap *map,
138
+ struct hashmap_entry *removed_entry)
139
+ {
140
+ size_t i;
141
+ #ifdef HASHMAP_METRICS
142
+ size_t removed_i = 0;
143
+ #endif
144
+ size_t index;
145
+ size_t entry_index;
146
+ size_t removed_index = (removed_entry - map->table);
147
+ struct hashmap_entry *entry;
148
+
149
+ /* Free the key */
150
+ if (map->key_free) {
151
+ map->key_free(removed_entry->key);
152
+ }
153
+ --map->num_entries;
154
+
155
+ /* Fill the free slot in the chain */
156
+ index = HASHMAP_PROBE_NEXT(map, removed_index);
157
+ for (i = 1; i < map->table_size; ++i) {
158
+ entry = &map->table[index];
159
+ if (!entry->key) {
160
+ /* Reached end of chain */
161
+ break;
162
+ }
163
+ entry_index = hashmap_calc_index(map, entry->key);
164
+ /* Shift in entries with an index <= to the removed slot */
165
+ if (HASHMAP_INDEX_LE(map, removed_index, entry_index)) {
166
+ #ifdef HASHMAP_METRICS
167
+ entry->num_collisions -= (i - removed_i);
168
+ removed_i = i;
169
+ #endif
170
+ memcpy(removed_entry, entry, sizeof(*removed_entry));
171
+ removed_index = index;
172
+ removed_entry = entry;
173
+ }
174
+ index = HASHMAP_PROBE_NEXT(map, index);
175
+ }
176
+ /* Clear the last removed entry */
177
+ memset(removed_entry, 0, sizeof(*removed_entry));
178
+ }
179
+
180
+ /*
181
+ * Reallocates the hash table to the new size and rehashes all entries.
182
+ * new_size MUST be a power of 2.
183
+ * Returns 0 on success and -errno on allocation or hash function failure.
184
+ */
185
+ static int hashmap_rehash(struct hashmap *map, size_t new_size)
186
+ {
187
+ size_t old_size;
188
+ struct hashmap_entry *old_table;
189
+ struct hashmap_entry *new_table;
190
+ struct hashmap_entry *entry;
191
+ struct hashmap_entry *new_entry;
192
+
193
+ HASHMAP_ASSERT(new_size >= HASHMAP_SIZE_MIN);
194
+ HASHMAP_ASSERT((new_size & (new_size - 1)) == 0);
195
+
196
+ new_table = (struct hashmap_entry *)calloc(new_size,
197
+ sizeof(struct hashmap_entry));
198
+ if (!new_table) {
199
+ return -ENOMEM;
200
+ }
201
+ /* Backup old elements in case of rehash failure */
202
+ old_size = map->table_size;
203
+ old_table = map->table;
204
+ map->table_size = new_size;
205
+ map->table = new_table;
206
+ /* Rehash */
207
+ for (entry = old_table; entry < &old_table[old_size]; ++entry) {
208
+ if (!entry->data) {
209
+ /* Only copy entries with data */
210
+ continue;
211
+ }
212
+ new_entry = hashmap_entry_find(map, entry->key, true);
213
+ if (!new_entry) {
214
+ /*
215
+ * The load factor is too high with the new table
216
+ * size, or a poor hash function was used.
217
+ */
218
+ goto revert;
219
+ }
220
+ /* Shallow copy (intentionally omits num_collisions) */
221
+ new_entry->key = entry->key;
222
+ new_entry->data = entry->data;
223
+ }
224
+ free(old_table);
225
+ return 0;
226
+ revert:
227
+ map->table_size = old_size;
228
+ map->table = old_table;
229
+ free(new_table);
230
+ return -EINVAL;
231
+ }
232
+
233
+ /*
234
+ * Iterate through all entries and free all keys.
235
+ */
236
+ static void hashmap_free_keys(struct hashmap *map)
237
+ {
238
+ struct hashmap_iter *iter;
239
+
240
+ if (!map->key_free) {
241
+ return;
242
+ }
243
+ for (iter = hashmap_iter(map); iter;
244
+ iter = hashmap_iter_next(map, iter)) {
245
+ map->key_free((void *)hashmap_iter_get_key(iter));
246
+ }
247
+ }
248
+
249
+ /*
250
+ * Initialize an empty hashmap. A hash function and a key comparator are
251
+ * required.
252
+ *
253
+ * hash_func should return an even distribution of numbers between 0
254
+ * and SIZE_MAX varying on the key provided.
255
+ *
256
+ * key_compare_func should return 0 if the keys match, and non-zero otherwise.
257
+ *
258
+ * initial_size is optional, and may be set to the max number of entries
259
+ * expected to be put in the hash table. This is used as a hint to
260
+ * pre-allocate the hash table to the minimum size needed to avoid
261
+ * gratuitous rehashes. If initial_size 0, a default size will be used.
262
+ *
263
+ * Returns 0 on success and -errno on failure.
264
+ */
265
+ int hashmap_init(struct hashmap *map, size_t (*hash_func)(const void *),
266
+ int (*key_compare_func)(const void *, const void *),
267
+ size_t initial_size)
268
+ {
269
+ HASHMAP_ASSERT(map != NULL);
270
+ HASHMAP_ASSERT(hash_func != NULL);
271
+ HASHMAP_ASSERT(key_compare_func != NULL);
272
+
273
+ if (!initial_size) {
274
+ initial_size = HASHMAP_SIZE_DEFAULT;
275
+ } else {
276
+ /* Convert init size to valid table size */
277
+ initial_size = hashmap_table_size_calc(initial_size);
278
+ }
279
+ map->table_size_init = initial_size;
280
+ map->table_size = initial_size;
281
+ map->num_entries = 0;
282
+ map->table = (struct hashmap_entry *)calloc(initial_size,
283
+ sizeof(struct hashmap_entry));
284
+ if (!map->table) {
285
+ return -ENOMEM;
286
+ }
287
+ map->hash = hash_func;
288
+ map->key_compare = key_compare_func;
289
+ map->key_alloc = NULL;
290
+ map->key_free = NULL;
291
+ return 0;
292
+ }
293
+
294
+ /*
295
+ * Free the hashmap and all associated memory.
296
+ */
297
+ void hashmap_destroy(struct hashmap *map)
298
+ {
299
+ if (!map) {
300
+ return;
301
+ }
302
+ hashmap_free_keys(map);
303
+ free(map->table);
304
+ memset(map, 0, sizeof(*map));
305
+ }
306
+
307
+ /*
308
+ * Enable internal memory management of hash keys.
309
+ */
310
+ void hashmap_set_key_alloc_funcs(struct hashmap *map,
311
+ void *(*key_alloc_func)(const void *),
312
+ void (*key_free_func)(void *))
313
+ {
314
+ HASHMAP_ASSERT(map != NULL);
315
+
316
+ map->key_alloc = key_alloc_func;
317
+ map->key_free = key_free_func;
318
+ }
319
+
320
+ /*
321
+ * Add an entry to the hashmap. If an entry with a matching key already
322
+ * exists and has a data pointer associated with it, the existing data
323
+ * pointer is returned, instead of assigning the new value. Compare
324
+ * the return value with the data passed in to determine if a new entry was
325
+ * created. Returns NULL if memory allocation failed.
326
+ */
327
+ void *hashmap_put(struct hashmap *map, const void *key, void *data)
328
+ {
329
+ struct hashmap_entry *entry;
330
+
331
+ HASHMAP_ASSERT(map != NULL);
332
+ HASHMAP_ASSERT(key != NULL);
333
+
334
+ /* Rehash with 2x capacity if load factor is approaching 0.75 */
335
+ if (map->table_size <= hashmap_table_min_size_calc(map->num_entries)) {
336
+ hashmap_rehash(map, map->table_size << 1);
337
+ }
338
+ entry = hashmap_entry_find(map, key, true);
339
+ if (!entry) {
340
+ /*
341
+ * Cannot find an empty slot. Either out of memory, or using
342
+ * a poor hash function. Attempt to rehash once to reduce
343
+ * chain length.
344
+ */
345
+ if (hashmap_rehash(map, map->table_size << 1) < 0) {
346
+ return NULL;
347
+ }
348
+ entry = hashmap_entry_find(map, key, true);
349
+ if (!entry) {
350
+ return NULL;
351
+ }
352
+ }
353
+ if (!entry->key) {
354
+ /* Allocate copy of key to simplify memory management */
355
+ if (map->key_alloc) {
356
+ entry->key = map->key_alloc(key);
357
+ if (!entry->key) {
358
+ return NULL;
359
+ }
360
+ } else {
361
+ entry->key = (void *)key;
362
+ }
363
+ ++map->num_entries;
364
+ } else if (entry->data) {
365
+ /* Do not overwrite existing data */
366
+ return entry->data;
367
+ }
368
+ entry->data = data;
369
+ return data;
370
+ }
371
+
372
+ /*
373
+ * Return the data pointer, or NULL if no entry exists.
374
+ */
375
+ void *hashmap_get(const struct hashmap *map, const void *key)
376
+ {
377
+ struct hashmap_entry *entry;
378
+
379
+ HASHMAP_ASSERT(map != NULL);
380
+ HASHMAP_ASSERT(key != NULL);
381
+
382
+ entry = hashmap_entry_find(map, key, false);
383
+ if (!entry) {
384
+ return NULL;
385
+ }
386
+ return entry->data;
387
+ }
388
+
389
+ /*
390
+ * Remove an entry with the specified key from the map.
391
+ * Returns the data pointer, or NULL, if no entry was found.
392
+ */
393
+ void *hashmap_remove(struct hashmap *map, const void *key)
394
+ {
395
+ struct hashmap_entry *entry;
396
+ void *data;
397
+
398
+ HASHMAP_ASSERT(map != NULL);
399
+ HASHMAP_ASSERT(key != NULL);
400
+
401
+ entry = hashmap_entry_find(map, key, false);
402
+ if (!entry) {
403
+ return NULL;
404
+ }
405
+ data = entry->data;
406
+ /* Clear the entry and make the chain contiguous */
407
+ hashmap_entry_remove(map, entry);
408
+ return data;
409
+ }
410
+
411
+ /*
412
+ * Remove all entries.
413
+ */
414
+ void hashmap_clear(struct hashmap *map)
415
+ {
416
+ HASHMAP_ASSERT(map != NULL);
417
+
418
+ hashmap_free_keys(map);
419
+ map->num_entries = 0;
420
+ memset(map->table, 0, sizeof(struct hashmap_entry) * map->table_size);
421
+ }
422
+
423
+ /*
424
+ * Remove all entries and reset the hash table to its initial size.
425
+ */
426
+ void hashmap_reset(struct hashmap *map)
427
+ {
428
+ struct hashmap_entry *new_table;
429
+
430
+ HASHMAP_ASSERT(map != NULL);
431
+
432
+ hashmap_clear(map);
433
+ if (map->table_size == map->table_size_init) {
434
+ return;
435
+ }
436
+ new_table = (struct hashmap_entry *)realloc(map->table,
437
+ sizeof(struct hashmap_entry) * map->table_size_init);
438
+ if (!new_table) {
439
+ return;
440
+ }
441
+ map->table = new_table;
442
+ map->table_size = map->table_size_init;
443
+ }
444
+
445
+ /*
446
+ * Return the number of entries in the hash map.
447
+ */
448
+ size_t hashmap_size(const struct hashmap *map)
449
+ {
450
+ HASHMAP_ASSERT(map != NULL);
451
+
452
+ return map->num_entries;
453
+ }
454
+
455
+ /*
456
+ * Get a new hashmap iterator. The iterator is an opaque
457
+ * pointer that may be used with hashmap_iter_*() functions.
458
+ * Hashmap iterators are INVALID after a put or remove operation is performed.
459
+ * hashmap_iter_remove() allows safe removal during iteration.
460
+ */
461
+ struct hashmap_iter *hashmap_iter(const struct hashmap *map)
462
+ {
463
+ HASHMAP_ASSERT(map != NULL);
464
+
465
+ if (!map->num_entries) {
466
+ return NULL;
467
+ }
468
+ return (struct hashmap_iter *)hashmap_entry_get_populated(map,
469
+ map->table);
470
+ }
471
+
472
+ /*
473
+ * Return an iterator to the next hashmap entry. Returns NULL if there are
474
+ * no more entries.
475
+ */
476
+ struct hashmap_iter *hashmap_iter_next(const struct hashmap *map,
477
+ const struct hashmap_iter *iter)
478
+ {
479
+ struct hashmap_entry *entry = (struct hashmap_entry *)iter;
480
+
481
+ HASHMAP_ASSERT(map != NULL);
482
+
483
+ if (!iter) {
484
+ return NULL;
485
+ }
486
+ return (struct hashmap_iter *)hashmap_entry_get_populated(map,
487
+ entry + 1);
488
+ }
489
+
490
+ /*
491
+ * Remove the hashmap entry pointed to by this iterator and return an
492
+ * iterator to the next entry. Returns NULL if there are no more entries.
493
+ */
494
+ struct hashmap_iter *hashmap_iter_remove(struct hashmap *map,
495
+ const struct hashmap_iter *iter)
496
+ {
497
+ struct hashmap_entry *entry = (struct hashmap_entry *)iter;
498
+
499
+ HASHMAP_ASSERT(map != NULL);
500
+
501
+ if (!iter) {
502
+ return NULL;
503
+ }
504
+ if (!entry->key) {
505
+ /* Iterator is invalid, so just return the next valid entry */
506
+ return hashmap_iter_next(map, iter);
507
+ }
508
+ hashmap_entry_remove(map, entry);
509
+ return (struct hashmap_iter *)hashmap_entry_get_populated(map, entry);
510
+ }
511
+
512
+ /*
513
+ * Return the key of the entry pointed to by the iterator.
514
+ */
515
+ const void *hashmap_iter_get_key(const struct hashmap_iter *iter)
516
+ {
517
+ if (!iter) {
518
+ return NULL;
519
+ }
520
+ return (const void *)((struct hashmap_entry *)iter)->key;
521
+ }
522
+
523
+ /*
524
+ * Return the data of the entry pointed to by the iterator.
525
+ */
526
+ void *hashmap_iter_get_data(const struct hashmap_iter *iter)
527
+ {
528
+ if (!iter) {
529
+ return NULL;
530
+ }
531
+ return ((struct hashmap_entry *)iter)->data;
532
+ }
533
+
534
+ /*
535
+ * Set the data pointer of the entry pointed to by the iterator.
536
+ */
537
+ void hashmap_iter_set_data(const struct hashmap_iter *iter, void *data)
538
+ {
539
+ if (!iter) {
540
+ return;
541
+ }
542
+ ((struct hashmap_entry *)iter)->data = data;
543
+ }
544
+
545
+ /*
546
+ * Invoke func for each entry in the hashmap. Unlike the hashmap_iter_*()
547
+ * interface, this function supports calls to hashmap_remove() during iteration.
548
+ * However, it is an error to put or remove an entry other than the current one,
549
+ * and doing so will immediately halt iteration and return an error.
550
+ * Iteration is stopped if func returns non-zero. Returns func's return
551
+ * value if it is < 0, otherwise, 0.
552
+ */
553
+ int hashmap_foreach(const struct hashmap *map,
554
+ int (*func)(const void *, void *, void *), void *arg)
555
+ {
556
+ struct hashmap_entry *entry;
557
+ size_t num_entries;
558
+ const void *key;
559
+ int rc;
560
+
561
+ HASHMAP_ASSERT(map != NULL);
562
+ HASHMAP_ASSERT(func != NULL);
563
+
564
+ entry = map->table;
565
+ for (entry = map->table; entry < &map->table[map->table_size];
566
+ ++entry) {
567
+ if (!entry->key) {
568
+ continue;
569
+ }
570
+ num_entries = map->num_entries;
571
+ key = entry->key;
572
+ rc = func(entry->key, entry->data, arg);
573
+ if (rc < 0) {
574
+ return rc;
575
+ }
576
+ if (rc > 0) {
577
+ return 0;
578
+ }
579
+ /* Run this entry again if func() deleted it */
580
+ if (entry->key != key) {
581
+ --entry;
582
+ } else if (num_entries != map->num_entries) {
583
+ /* Stop immediately if func put/removed another entry */
584
+ return -1;
585
+ }
586
+ }
587
+ return 0;
588
+ }
589
+
590
+ /*
591
+ * Default hash function for string keys.
592
+ * This is an implementation of the well-documented Jenkins one-at-a-time
593
+ * hash function.
594
+ */
595
+ size_t hashmap_hash_string(const void *key)
596
+ {
597
+ const char *key_str = (const char *)key;
598
+ size_t hash = 0;
599
+
600
+ for (; *key_str; ++key_str) {
601
+ hash += *key_str;
602
+ hash += (hash << 10);
603
+ hash ^= (hash >> 6);
604
+ }
605
+ hash += (hash << 3);
606
+ hash ^= (hash >> 11);
607
+ hash += (hash << 15);
608
+ return hash;
609
+ }
610
+
611
+ /*
612
+ * Default key comparator function for string keys.
613
+ */
614
+ int hashmap_compare_string(const void *a, const void *b)
615
+ {
616
+ return strcmp((const char *)a, (const char *)b);
617
+ }
618
+
619
+ /*
620
+ * Default key allocation function for string keys. Use free() for the
621
+ * key_free_func.
622
+ */
623
+ void *hashmap_alloc_key_string(const void *key)
624
+ {
625
+ return (void *)strdup((const char *)key);
626
+ }
627
+
628
+ #ifdef HASHMAP_METRICS
629
+ /*
630
+ * Return the load factor.
631
+ */
632
+ double hashmap_load_factor(const struct hashmap *map)
633
+ {
634
+ HASHMAP_ASSERT(map != NULL);
635
+
636
+ if (!map->table_size) {
637
+ return 0;
638
+ }
639
+ return (double)map->num_entries / map->table_size;
640
+ }
641
+
642
+ /*
643
+ * Return the average number of collisions per entry.
644
+ */
645
+ double hashmap_collisions_mean(const struct hashmap *map)
646
+ {
647
+ struct hashmap_entry *entry;
648
+ size_t total_collisions = 0;
649
+
650
+ HASHMAP_ASSERT(map != NULL);
651
+
652
+ if (!map->num_entries) {
653
+ return 0;
654
+ }
655
+ for (entry = map->table; entry < &map->table[map->table_size];
656
+ ++entry) {
657
+ if (!entry->key) {
658
+ continue;
659
+ }
660
+ total_collisions += entry->num_collisions;
661
+ }
662
+ return (double)total_collisions / map->num_entries;
663
+ }
664
+
665
+ /*
666
+ * Return the variance between entry collisions. The higher the variance,
667
+ * the more likely the hash function is poor and is resulting in clustering.
668
+ */
669
+ double hashmap_collisions_variance(const struct hashmap *map)
670
+ {
671
+ struct hashmap_entry *entry;
672
+ double mean_collisions;
673
+ double variance;
674
+ double total_variance = 0;
675
+
676
+ HASHMAP_ASSERT(map != NULL);
677
+
678
+ if (!map->num_entries) {
679
+ return 0;
680
+ }
681
+ mean_collisions = hashmap_collisions_mean(map);
682
+ for (entry = map->table; entry < &map->table[map->table_size];
683
+ ++entry) {
684
+ if (!entry->key) {
685
+ continue;
686
+ }
687
+ variance = (double)entry->num_collisions - mean_collisions;
688
+ total_variance += variance * variance;
689
+ }
690
+ return total_variance / map->num_entries;
691
+ }
692
+ #endif
@@ -0,0 +1,314 @@
1
+ #include "jsmn.h"
2
+
3
+ /**
4
+ * Allocates a fresh unused token from the token pull.
5
+ */
6
+ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
7
+ jsmntok_t *tokens, size_t num_tokens) {
8
+ jsmntok_t *tok;
9
+ if (parser->toknext >= num_tokens) {
10
+ return NULL;
11
+ }
12
+ tok = &tokens[parser->toknext++];
13
+ tok->start = tok->end = -1;
14
+ tok->size = 0;
15
+ #ifdef JSMN_PARENT_LINKS
16
+ tok->parent = -1;
17
+ #endif
18
+ return tok;
19
+ }
20
+
21
+ /**
22
+ * Fills token type and boundaries.
23
+ */
24
+ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
25
+ int start, int end) {
26
+ token->type = type;
27
+ token->start = start;
28
+ token->end = end;
29
+ token->size = 0;
30
+ }
31
+
32
+ /**
33
+ * Fills next available token with JSON primitive.
34
+ */
35
+ static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
36
+ size_t len, jsmntok_t *tokens, size_t num_tokens) {
37
+ jsmntok_t *token;
38
+ int start;
39
+
40
+ start = parser->pos;
41
+
42
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
43
+ switch (js[parser->pos]) {
44
+ #ifndef JSMN_STRICT
45
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
46
+ case ':':
47
+ #endif
48
+ case '\t' : case '\r' : case '\n' : case ' ' :
49
+ case ',' : case ']' : case '}' :
50
+ goto found;
51
+ }
52
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
53
+ parser->pos = start;
54
+ return JSMN_ERROR_INVAL;
55
+ }
56
+ }
57
+ #ifdef JSMN_STRICT
58
+ /* In strict mode primitive must be followed by a comma/object/array */
59
+ parser->pos = start;
60
+ return JSMN_ERROR_PART;
61
+ #endif
62
+
63
+ found:
64
+ if (tokens == NULL) {
65
+ parser->pos--;
66
+ return 0;
67
+ }
68
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
69
+ if (token == NULL) {
70
+ parser->pos = start;
71
+ return JSMN_ERROR_NOMEM;
72
+ }
73
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
74
+ #ifdef JSMN_PARENT_LINKS
75
+ token->parent = parser->toksuper;
76
+ #endif
77
+ parser->pos--;
78
+ return 0;
79
+ }
80
+
81
+ /**
82
+ * Fills next token with JSON string.
83
+ */
84
+ static int jsmn_parse_string(jsmn_parser *parser, const char *js,
85
+ size_t len, jsmntok_t *tokens, size_t num_tokens) {
86
+ jsmntok_t *token;
87
+
88
+ int start = parser->pos;
89
+
90
+ parser->pos++;
91
+
92
+ /* Skip starting quote */
93
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
94
+ char c = js[parser->pos];
95
+
96
+ /* Quote: end of string */
97
+ if (c == '\"') {
98
+ if (tokens == NULL) {
99
+ return 0;
100
+ }
101
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
102
+ if (token == NULL) {
103
+ parser->pos = start;
104
+ return JSMN_ERROR_NOMEM;
105
+ }
106
+ jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
107
+ #ifdef JSMN_PARENT_LINKS
108
+ token->parent = parser->toksuper;
109
+ #endif
110
+ return 0;
111
+ }
112
+
113
+ /* Backslash: Quoted symbol expected */
114
+ if (c == '\\' && parser->pos + 1 < len) {
115
+ int i;
116
+ parser->pos++;
117
+ switch (js[parser->pos]) {
118
+ /* Allowed escaped symbols */
119
+ case '\"': case '/' : case '\\' : case 'b' :
120
+ case 'f' : case 'r' : case 'n' : case 't' :
121
+ break;
122
+ /* Allows escaped symbol \uXXXX */
123
+ case 'u':
124
+ parser->pos++;
125
+ for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
126
+ /* If it isn't a hex character we have an error */
127
+ if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
128
+ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
129
+ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
130
+ parser->pos = start;
131
+ return JSMN_ERROR_INVAL;
132
+ }
133
+ parser->pos++;
134
+ }
135
+ parser->pos--;
136
+ break;
137
+ /* Unexpected symbol */
138
+ default:
139
+ parser->pos = start;
140
+ return JSMN_ERROR_INVAL;
141
+ }
142
+ }
143
+ }
144
+ parser->pos = start;
145
+ return JSMN_ERROR_PART;
146
+ }
147
+
148
+ /**
149
+ * Parse JSON string and fill tokens.
150
+ */
151
+ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
152
+ jsmntok_t *tokens, unsigned int num_tokens) {
153
+ int r;
154
+ int i;
155
+ jsmntok_t *token;
156
+ int count = parser->toknext;
157
+
158
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
159
+ char c;
160
+ jsmntype_t type;
161
+
162
+ c = js[parser->pos];
163
+ switch (c) {
164
+ case '{': case '[':
165
+ count++;
166
+ if (tokens == NULL) {
167
+ break;
168
+ }
169
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
170
+ if (token == NULL)
171
+ return JSMN_ERROR_NOMEM;
172
+ if (parser->toksuper != -1) {
173
+ tokens[parser->toksuper].size++;
174
+ #ifdef JSMN_PARENT_LINKS
175
+ token->parent = parser->toksuper;
176
+ #endif
177
+ }
178
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
179
+ token->start = parser->pos;
180
+ parser->toksuper = parser->toknext - 1;
181
+ break;
182
+ case '}': case ']':
183
+ if (tokens == NULL)
184
+ break;
185
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
186
+ #ifdef JSMN_PARENT_LINKS
187
+ if (parser->toknext < 1) {
188
+ return JSMN_ERROR_INVAL;
189
+ }
190
+ token = &tokens[parser->toknext - 1];
191
+ for (;;) {
192
+ if (token->start != -1 && token->end == -1) {
193
+ if (token->type != type) {
194
+ return JSMN_ERROR_INVAL;
195
+ }
196
+ token->end = parser->pos + 1;
197
+ parser->toksuper = token->parent;
198
+ break;
199
+ }
200
+ if (token->parent == -1) {
201
+ if(token->type != type || parser->toksuper == -1) {
202
+ return JSMN_ERROR_INVAL;
203
+ }
204
+ break;
205
+ }
206
+ token = &tokens[token->parent];
207
+ }
208
+ #else
209
+ for (i = parser->toknext - 1; i >= 0; i--) {
210
+ token = &tokens[i];
211
+ if (token->start != -1 && token->end == -1) {
212
+ if (token->type != type) {
213
+ return JSMN_ERROR_INVAL;
214
+ }
215
+ parser->toksuper = -1;
216
+ token->end = parser->pos + 1;
217
+ break;
218
+ }
219
+ }
220
+ /* Error if unmatched closing bracket */
221
+ if (i == -1) return JSMN_ERROR_INVAL;
222
+ for (; i >= 0; i--) {
223
+ token = &tokens[i];
224
+ if (token->start != -1 && token->end == -1) {
225
+ parser->toksuper = i;
226
+ break;
227
+ }
228
+ }
229
+ #endif
230
+ break;
231
+ case '\"':
232
+ r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
233
+ if (r < 0) return r;
234
+ count++;
235
+ if (parser->toksuper != -1 && tokens != NULL)
236
+ tokens[parser->toksuper].size++;
237
+ break;
238
+ case '\t' : case '\r' : case '\n' : case ' ':
239
+ break;
240
+ case ':':
241
+ parser->toksuper = parser->toknext - 1;
242
+ break;
243
+ case ',':
244
+ if (tokens != NULL && parser->toksuper != -1 &&
245
+ tokens[parser->toksuper].type != JSMN_ARRAY &&
246
+ tokens[parser->toksuper].type != JSMN_OBJECT) {
247
+ #ifdef JSMN_PARENT_LINKS
248
+ parser->toksuper = tokens[parser->toksuper].parent;
249
+ #else
250
+ for (i = parser->toknext - 1; i >= 0; i--) {
251
+ if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
252
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
253
+ parser->toksuper = i;
254
+ break;
255
+ }
256
+ }
257
+ }
258
+ #endif
259
+ }
260
+ break;
261
+ #ifdef JSMN_STRICT
262
+ /* In strict mode primitives are: numbers and booleans */
263
+ case '-': case '0': case '1' : case '2': case '3' : case '4':
264
+ case '5': case '6': case '7' : case '8': case '9':
265
+ case 't': case 'f': case 'n' :
266
+ /* And they must not be keys of the object */
267
+ if (tokens != NULL && parser->toksuper != -1) {
268
+ jsmntok_t *t = &tokens[parser->toksuper];
269
+ if (t->type == JSMN_OBJECT ||
270
+ (t->type == JSMN_STRING && t->size != 0)) {
271
+ return JSMN_ERROR_INVAL;
272
+ }
273
+ }
274
+ #else
275
+ /* In non-strict mode every unquoted value is a primitive */
276
+ default:
277
+ #endif
278
+ r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
279
+ if (r < 0) return r;
280
+ count++;
281
+ if (parser->toksuper != -1 && tokens != NULL)
282
+ tokens[parser->toksuper].size++;
283
+ break;
284
+
285
+ #ifdef JSMN_STRICT
286
+ /* Unexpected char in strict mode */
287
+ default:
288
+ return JSMN_ERROR_INVAL;
289
+ #endif
290
+ }
291
+ }
292
+
293
+ if (tokens != NULL) {
294
+ for (i = parser->toknext - 1; i >= 0; i--) {
295
+ /* Unmatched opened object or array */
296
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
297
+ return JSMN_ERROR_PART;
298
+ }
299
+ }
300
+ }
301
+
302
+ return count;
303
+ }
304
+
305
+ /**
306
+ * Creates a new parser based over a given buffer with an array of tokens
307
+ * available.
308
+ */
309
+ void jsmn_init(jsmn_parser *parser) {
310
+ parser->pos = 0;
311
+ parser->toknext = 0;
312
+ parser->toksuper = -1;
313
+ }
314
+
Binary file
data/lib/mmap.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'weak_ref'
2
+
3
+ class FastMmapedFile
4
+ def track_ref!
5
+
6
+ end
7
+ end
@@ -0,0 +1,58 @@
1
+ require 'prometheus/client/registry'
2
+ require 'prometheus/client/configuration'
3
+ require 'prometheus/client/mmaped_value'
4
+
5
+ module Prometheus
6
+ # Client is a ruby implementation for a Prometheus compatible client.
7
+ module Client
8
+ class << self
9
+ attr_writer :configuration
10
+
11
+ def configuration
12
+ @configuration ||= Configuration.new
13
+ end
14
+
15
+ def configure
16
+ yield(configuration)
17
+ end
18
+ o
19
+ # Returns a default registry object
20
+ def registry
21
+ @registry ||= Registry.new
22
+ end
23
+
24
+ def logger
25
+ configuration.logger
26
+ end
27
+
28
+ def pid
29
+ configuration.pid_provider.call
30
+ end
31
+
32
+ # Resets the registry and reinitializes all metrics files.
33
+ # Use case: clean up everything in specs `before` block,
34
+ # to prevent leaking the state between specs which are updating metrics.
35
+ def reset!
36
+ @registry = nil
37
+ ::Prometheus::Client::MmapedValue.reset_and_reinitialize
38
+ end
39
+
40
+ def cleanup!
41
+ Dir.glob("#{configuration.multiprocess_files_dir}/*.db").each { |f| File.unlink(f) if File.exist?(f) }
42
+ end
43
+
44
+ # With `force: false`: reinitializes metric files only for processes with the changed PID.
45
+ # With `force: true`: reinitializes all metrics files.
46
+ # Always keeps the registry.
47
+ # Use case (`force: false`): pick up new metric files on each worker start,
48
+ # without resetting already registered files for the master or previously initialized workers.
49
+ def reinitialize_on_pid_change(force: false)
50
+ if force
51
+ ::Prometheus::Client::MmapedValue.reset_and_reinitialize
52
+ else
53
+ ::Prometheus::Client::MmapedValue.reinitialize_on_pid_change
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,12 +1,15 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ require 'base64'
3
4
  require 'thread'
4
5
  require 'net/http'
5
6
  require 'uri'
6
7
  require 'erb'
8
+ require 'set'
7
9
 
8
10
  require 'prometheus/client'
9
11
  require 'prometheus/client/formats/text'
12
+ require 'prometheus/client/label_set_validator'
10
13
 
11
14
  module Prometheus
12
15
  # Client is a ruby implementation for a Prometheus compatible client.
@@ -14,23 +17,31 @@ module Prometheus
14
17
  # Push implements a simple way to transmit a given registry to a given
15
18
  # Pushgateway.
16
19
  class Push
20
+ class HttpError < StandardError; end
21
+ class HttpRedirectError < HttpError; end
22
+ class HttpClientError < HttpError; end
23
+ class HttpServerError < HttpError; end
24
+
17
25
  DEFAULT_GATEWAY = 'http://localhost:9091'.freeze
18
26
  PATH = '/metrics/job/%s'.freeze
19
- INSTANCE_PATH = '/metrics/job/%s/instance/%s'.freeze
20
27
  SUPPORTED_SCHEMES = %w(http https).freeze
21
28
 
22
- attr_reader :job, :instance, :gateway, :path
29
+ attr_reader :job, :gateway, :path
23
30
 
24
- def initialize(job:, instance: nil, gateway: DEFAULT_GATEWAY, **kwargs)
31
+ def initialize(job:, gateway: DEFAULT_GATEWAY, grouping_key: {}, **kwargs)
25
32
  raise ArgumentError, "job cannot be nil" if job.nil?
26
33
  raise ArgumentError, "job cannot be empty" if job.empty?
34
+ @validator = LabelSetValidator.new()
35
+ @validator.validate(grouping_key)
27
36
 
28
37
  @mutex = Mutex.new
29
38
  @job = job
30
- @instance = instance
31
39
  @gateway = gateway || DEFAULT_GATEWAY
32
- @path = build_path(job, instance)
40
+ @grouping_key = grouping_key
41
+ @path = build_path(job, grouping_key)
42
+
33
43
  @uri = parse("#{@gateway}#{@path}")
44
+ validate_no_basic_auth!(@uri)
34
45
 
35
46
  @http = Net::HTTP.new(@uri.host, @uri.port)
36
47
  @http.use_ssl = (@uri.scheme == 'https')
@@ -38,6 +49,11 @@ module Prometheus
38
49
  @http.read_timeout = kwargs[:read_timeout] if kwargs[:read_timeout]
39
50
  end
40
51
 
52
+ def basic_auth(user, password)
53
+ @user = user
54
+ @password = password
55
+ end
56
+
41
57
  def add(registry)
42
58
  synchronize do
43
59
  request(Net::HTTP::Post, registry)
@@ -70,26 +86,118 @@ module Prometheus
70
86
  raise ArgumentError, "#{url} is not a valid URL: #{e}"
71
87
  end
72
88
 
73
- def build_path(job, instance)
74
- if instance && !instance.empty?
75
- format(INSTANCE_PATH, ERB::Util::url_encode(job), ERB::Util::url_encode(instance))
76
- else
77
- format(PATH, ERB::Util::url_encode(job))
89
+ def build_path(job, grouping_key)
90
+ path = format(PATH, ERB::Util::url_encode(job))
91
+
92
+ grouping_key.each do |label, value|
93
+ if value.include?('/')
94
+ encoded_value = Base64.urlsafe_encode64(value)
95
+ path += "/#{label}@base64/#{encoded_value}"
96
+ # While it's valid for the urlsafe_encode64 function to return an
97
+ # empty string when the input string is empty, it doesn't work for
98
+ # our specific use case as we're putting the result into a URL path
99
+ # segment. A double slash (`//`) can be normalised away by HTTP
100
+ # libraries, proxies, and web servers.
101
+ #
102
+ # For empty strings, we use a single padding character (`=`) as the
103
+ # value.
104
+ #
105
+ # See the pushgateway docs for more details:
106
+ #
107
+ # https://github.com/prometheus/pushgateway/blob/6393a901f56d4dda62cd0f6ab1f1f07c495b6354/README.md#url
108
+ elsif value.empty?
109
+ path += "/#{label}@base64/="
110
+ else
111
+ path += "/#{label}/#{ERB::Util::url_encode(value)}"
112
+ end
78
113
  end
114
+
115
+ path
79
116
  end
80
117
 
81
118
  def request(req_class, registry = nil)
119
+ validate_no_label_clashes!(registry) if registry
120
+
82
121
  req = req_class.new(@uri)
83
122
  req.content_type = Formats::Text::CONTENT_TYPE
84
- req.basic_auth(@uri.user, @uri.password) if @uri.user
123
+ req.basic_auth(@user, @password) if @user
85
124
  req.body = Formats::Text.marshal(registry) if registry
86
125
 
87
- @http.request(req)
126
+ response = @http.request(req)
127
+ validate_response!(response)
128
+
129
+ response
88
130
  end
89
131
 
90
132
  def synchronize
91
133
  @mutex.synchronize { yield }
92
134
  end
135
+
136
+ def validate_no_basic_auth!(uri)
137
+ if uri.user || uri.password
138
+ raise ArgumentError, <<~EOF
139
+ Setting Basic Auth credentials in the gateway URL is not supported, please call the `basic_auth` method.
140
+
141
+ Received username `#{uri.user}` in gateway URL. Instead of passing
142
+ Basic Auth credentials like this:
143
+
144
+ ```
145
+ push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://user:password@localhost:9091")
146
+ ```
147
+
148
+ please pass them like this:
149
+
150
+ ```
151
+ push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://localhost:9091")
152
+ push.basic_auth("user", "password")
153
+ ```
154
+
155
+ While URLs do support passing Basic Auth credentials using the
156
+ `http://user:password@example.com/` syntax, the username and
157
+ password in that syntax have to follow the usual rules for URL
158
+ encoding of characters per RFC 3986
159
+ (https://datatracker.ietf.org/doc/html/rfc3986#section-2.1).
160
+
161
+ Rather than place the burden of correctly performing that encoding
162
+ on users of this gem, we decided to have a separate method for
163
+ supplying Basic Auth credentials, with no requirement to URL encode
164
+ the characters in them.
165
+ EOF
166
+ end
167
+ end
168
+
169
+ def validate_no_label_clashes!(registry)
170
+ # There's nothing to check if we don't have a grouping key
171
+ return if @grouping_key.empty?
172
+
173
+ # We could be doing a lot of comparisons, so let's do them against a
174
+ # set rather than an array
175
+ grouping_key_labels = @grouping_key.keys.to_set
176
+
177
+ registry.metrics.each do |metric|
178
+ metric.values.keys.first.keys.each do |label|
179
+ if grouping_key_labels.include?(label)
180
+ raise LabelSetValidator::InvalidLabelSetError,
181
+ "label :#{label} from grouping key collides with label of the " \
182
+ "same name from metric :#{metric.name} and would overwrite it"
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ def validate_response!(response)
189
+ status = Integer(response.code)
190
+ if status >= 300
191
+ message = "status: #{response.code}, message: #{response.message}, body: #{response.body}"
192
+ if status <= 399
193
+ raise HttpRedirectError, message
194
+ elsif status <= 499
195
+ raise HttpClientError, message
196
+ else
197
+ raise HttpServerError, message
198
+ end
199
+ end
200
+ end
93
201
  end
94
202
  end
95
203
  end
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.17.0'.freeze
3
+ VERSION = '0.18.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client-mmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schmidt
8
8
  - Paweł Chojnacki
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-12-12 00:00:00.000000000 Z
12
+ date: 2023-01-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fuzzbert
@@ -87,7 +87,7 @@ dependencies:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: 0.16.2
90
- description:
90
+ description:
91
91
  email:
92
92
  - ts@soundcloud.com
93
93
  - pawel@gitlab.com
@@ -106,6 +106,8 @@ files:
106
106
  - ext/fast_mmaped_file/file_reading.c
107
107
  - ext/fast_mmaped_file/file_reading.h
108
108
  - ext/fast_mmaped_file/globals.h
109
+ - ext/fast_mmaped_file/hashmap.c
110
+ - ext/fast_mmaped_file/jsmn.c
109
111
  - ext/fast_mmaped_file/mmap.c
110
112
  - ext/fast_mmaped_file/mmap.h
111
113
  - ext/fast_mmaped_file/rendering.c
@@ -114,7 +116,10 @@ files:
114
116
  - ext/fast_mmaped_file/utils.h
115
117
  - ext/fast_mmaped_file/value_access.c
116
118
  - ext/fast_mmaped_file/value_access.h
119
+ - lib/fast_mmaped_file.bundle
120
+ - lib/mmap.rb
117
121
  - lib/prometheus.rb
122
+ - lib/prometheus/#client.rb#
118
123
  - lib/prometheus/client.rb
119
124
  - lib/prometheus/client/configuration.rb
120
125
  - lib/prometheus/client/counter.rb
@@ -163,7 +168,7 @@ homepage: https://gitlab.com/gitlab-org/prometheus-client-mmap
163
168
  licenses:
164
169
  - Apache-2.0
165
170
  metadata: {}
166
- post_install_message:
171
+ post_install_message:
167
172
  rdoc_options: []
168
173
  require_paths:
169
174
  - lib
@@ -178,8 +183,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
183
  - !ruby/object:Gem::Version
179
184
  version: '0'
180
185
  requirements: []
181
- rubygems_version: 3.2.33
182
- signing_key:
186
+ rubygems_version: 3.3.26
187
+ signing_key:
183
188
  specification_version: 4
184
189
  summary: A suite of instrumentation metric primitivesthat can be exposed through a
185
190
  web services interface.