File Coverage

blib/lib/Prometheus/Tiny/Shared.pm
Criterion Covered Total %
statement 97 97 100.0
branch 17 18 94.4
condition 46 54 85.1
subroutine 18 18 100.0
pod 8 8 100.0
total 186 195 95.3


line stmt bran cond sub pod time code
1             package Prometheus::Tiny::Shared;
2             $Prometheus::Tiny::Shared::VERSION = '0.027';
3             # ABSTRACT: A tiny Prometheus client with a shared database behind it
4              
5 19     19   1363048 use warnings;
  19         233  
  19         700  
6 19     19   113 use strict;
  19         37  
  19         485  
7              
8 19     19   9199 use Prometheus::Tiny 0.011;
  19         35232  
  19         563  
9 19     19   8253 use parent 'Prometheus::Tiny';
  19         5730  
  19         106  
10              
11 19     19   14586 use Hash::SharedMem qw(shash_open shash_get shash_set shash_cset shash_keys_array shash_group_get_hash);
  19         55157  
  19         1771  
12 19     19   14635 use JSON::XS qw(encode_json decode_json);
  19         140124  
  19         1214  
13 19     19   15426 use File::Temp qw(tempdir);
  19         373511  
  19         1245  
14 19     19   166 use File::Path qw(rmtree);
  19         40  
  19         897  
15 19     19   113 use Carp qw(croak carp);
  19         49  
  19         851  
16 19     19   117 use Scalar::Util qw(looks_like_number);
  19         44  
  19         24937  
17              
18             sub new {
19 56     56 1 32100 my ($class, %args) = @_;
20              
21 56 100       230 if (exists $args{cache_args}) {
22 1         17 croak <
23             The 'cache_args' argument to Prometheus::Tiny::Shared::new has been removed.
24             Read the docs for more info, and switch to the 'filename' argument.
25             EOF
26             }
27              
28 55         120 my $filename = delete $args{filename};
29 55   50     285 my $init_file = delete $args{init_file} || 0;
30              
31 55         330 my $self = $class->SUPER::new(%args);
32              
33 55 100       959 if ($filename) {
34 5 50       17 rmtree($filename) if $init_file;
35             }
36             else {
37 50         195 $filename = tempdir('pts-XXXXXXXX', TMPDIR => 1, CLEANUP => 1);
38             }
39              
40 55         40213 $self->{_shash} = shash_open($filename, 'rwc');
41              
42 55         577 return $self;
43             }
44              
45             sub set {
46 69     69 1 5273 my ($self, $name, $value, $labels, $timestamp) = @_;
47              
48 69 100       356 unless (looks_like_number $value) {
49 1         31 carp "setting '$name' to non-numeric value, using 0 instead";
50 1         613 $value = 0;
51             }
52              
53 69         299 my $key = join('-', 'k', $name, $self->_format_labels($labels));
54 69         9217 shash_set($self->{_shash}, $key, encode_json([$value, $timestamp]));
55              
56 69         398 return;
57             }
58              
59             sub add {
60 138     138 1 3151 my ($self, $name, $diff, $labels) = @_;
61              
62 138 100       527 unless (looks_like_number $diff) {
63 1         16 carp "adjusting '$name' by non-numeric value, adding 0 instead";
64 1         453 $diff = 0;
65             }
66              
67 138         345 my $key = join('-', 'k', $name, $self->_format_labels($labels));
68              
69 138         3749 my ($ov, $nv);
70              
71             do {
72 138         382 $ov = shash_get($self->{_shash}, $key);
73 138 100       315 if ($ov) {
74 89         269 my $ar = decode_json($ov);
75 89         172 $ar->[0] += $diff;
76 89         520 $nv = encode_json($ar);
77             }
78             else {
79 49         2500 $nv = encode_json([$diff]);
80             }
81 138         240 } until shash_cset($self->{_shash}, $key, $ov, $nv);
82              
83 138         427 return;
84             }
85              
86             sub clear {
87 1     1 1 948 my ($self, $name) = @_;
88              
89 1         2 for my $key (grep { substr($_, 0, 1) eq 'k' } @{shash_keys_array($self->{_shash})}) {
  4         12  
  1         8  
90 2         6 shash_set($self->{_shash}, $key, undef);
91             }
92              
93 1         4 return;
94             }
95              
96             sub declare {
97 29     29 1 7797 my ($self, $name, %meta) = @_;
98              
99 29         93 my $key = join('-', 'm', $name);
100 29         203 my $value = encode_json(\%meta);
101              
102 29 100       3449 return if shash_cset($self->{_shash}, $key, undef, $value);
103              
104 13         75 my $old = decode_json(shash_get($self->{_shash}, $key));
105              
106 13 100 100     189 if (
      100        
      66        
      33        
      66        
      100        
      100        
      66        
      100        
      100        
      100        
      100        
      100        
      100        
      66        
      100        
107             ((exists $old->{type} ^ exists $meta{type}) ||
108             (exists $old->{type} && $old->{type} ne $meta{type})) ||
109             ((exists $old->{help} ^ exists $meta{help}) ||
110             (exists $old->{help} && $old->{help} ne $meta{help})) ||
111             ((exists $old->{enum} ^ exists $meta{enum}) ||
112             (exists $old->{enum} && $old->{enum} ne $meta{enum})) ||
113             ((exists $old->{buckets} ^ exists $meta{buckets}) ||
114             (exists $old->{buckets} && (
115             @{$old->{buckets}} ne @{$meta{buckets}} ||
116             grep { $old->{buckets}[$_] != $meta{buckets}[$_] } (0 .. $#{$meta{buckets}})
117             ))
118             ) ||
119             ((exists $old->{enum_values} ^ exists $meta{enum_values}) ||
120             (exists $old->{enum_values} && (
121             @{$old->{enum_values}} ne @{$meta{enum_values}} ||
122             grep { $old->{enum_values}[$_] ne $meta{enum_values}[$_] } (0 .. $#{$meta{enum_values}})
123             ))
124             )
125             ) {
126 10         129 croak "redeclaration of '$name' with mismatched meta";
127             }
128              
129 3         18 return;
130             }
131              
132             sub histogram_observe {
133 9     9 1 105 my $self = shift;
134 9         26 my ($name) = @_;
135              
136 9         21 my $key = join('-', 'm', $name);
137              
138 9   100     72 $self->{meta}{$name} = decode_json(shash_get($self->{_shash}, $key) || '{}');
139              
140 9         35 return $self->SUPER::histogram_observe(@_);
141             }
142              
143             sub enum_set {
144 2     2 1 23 my $self = shift;
145 2         5 my ($name) = @_;
146              
147 2         7 my $key = join('-', 'm', $name);
148              
149 2   50     30 $self->{meta}{$name} = decode_json(shash_get($self->{_shash}, $key) || '{}');
150              
151 2         15 return $self->SUPER::enum_set(@_);
152             }
153              
154             sub format {
155 56     56 1 273230 my $self = shift;
156              
157 56         128 my (%metrics, %meta);
158              
159 56         451 my $hash = shash_group_get_hash($self->{_shash});
160 56         354 while ( my ($k, $v) = each %$hash ) {
161 133         453 my ($t, $name, $fmt) = split '-', $k, 3;
162 133 100       328 if ($t eq 'k') {
163 115         763 $metrics{$name}{$fmt} = decode_json($v);
164             }
165             else {
166 18         135 $meta{$name} = decode_json($v);
167             }
168             }
169 56         204 $self->{metrics} = \%metrics;
170 56         136 $self->{meta} = \%meta;
171              
172 56         310 return $self->SUPER::format(@_);
173             }
174              
175             1;
176              
177             __END__