File Coverage

blib/lib/Sub/Throttler/Limit.pm
Criterion Covered Total %
statement 72 73 98.6
branch 24 26 92.3
condition 7 10 70.0
subroutine 17 17 100.0
pod 8 8 100.0
total 128 134 95.5


line stmt bran cond sub pod time code
1             package Sub::Throttler::Limit;
2 7     7   1356777 use 5.010001;
  7         23  
3 7     7   30 use warnings;
  7         11  
  7         199  
4 7     7   42 use strict;
  7         7  
  7         150  
5 7     7   31 use utf8;
  7         8  
  7         41  
6 7     7   146 use Carp;
  7         10  
  7         655  
7             our @CARP_NOT = qw( Sub::Throttler Sub::Throttler::Periodic::EV );
8              
9             our $VERSION = 'v0.2.10';
10              
11 7     7   1097 use parent qw( Sub::Throttler::algo );
  7         627  
  7         74  
12 7     7   326 use Sub::Throttler qw( throttle_flush );
  7         10  
  7         26  
13              
14              
15             sub new {
16 7     7   616 use warnings FATAL => qw( misc );
  7         10  
  7         4623  
17 104     104 1 8193 my ($class, %opt) = @_;
18             my $self = bless {
19 103   100     945 limit => delete $opt{limit} // 1,
      33        
20             acquired=> {}, # { $id => { $key => $quantity, … }, … }
21             used => {}, # { $key => $quantity, … }
22             }, ref $class || $class;
23 103 100       704 croak 'limit must be an unsigned integer' if $self->{limit} !~ /\A\d+\z/ms;
24 101 100       236 croak 'bad param: '.(keys %opt)[0] if keys %opt;
25 99         446 return $self;
26             }
27              
28             sub acquire {
29 8     8 1 12 my ($self, $id, $key, $quantity) = @_;
30 8 50       10 if (!$self->try_acquire($id, $key, $quantity)) {
31 8         112 croak "$self: unable to acquire $quantity of resource '$key'";
32             }
33 0         0 return $self;
34             }
35              
36             sub limit {
37 34     34 1 6353 my ($self, $limit) = @_;
38 34 100       89 if (1 == @_) {
39 4         21 return $self->{limit};
40             }
41 30 50       159 croak 'limit must be an unsigned integer' if $limit !~ /\A\d+\z/ms;
42             # OPTIMIZATION call throttle_flush() only if amount of available
43             # resources increased (i.e. limit was increased)
44 30         83 my $resources_increases = $self->{limit} < $limit;
45 30         39 $self->{limit} = $limit;
46 30 100       57 if ($resources_increases) {
47 14         40 throttle_flush();
48             }
49 30         68 return $self;
50             }
51              
52             sub load {
53 3     3 1 131 my ($class, $state) = @_;
54 3 100       16 croak 'bad state: wrong algorithm' if $state->{algo} ne __PACKAGE__;
55 2         17 my $v = version->parse($state->{version});
56 2 100       16 if ($v > $VERSION) {
57 1         34 carp 'restoring state saved by future version';
58             }
59 2         257 my $self = $class->new(limit=>$state->{limit});
60 2         6 return $self;
61             }
62              
63             sub release {
64 75     75 1 298 return _release(@_);
65             }
66              
67             sub release_unused {
68 46     46 1 1489 return _release(@_);
69             }
70              
71             sub save {
72 3     3 1 5 my ($self) = @_;
73             my $state = {
74             algo => __PACKAGE__,
75             version => version->declare($VERSION)->numify,
76             limit => $self->{limit},
77             used => $self->{used},
78 3         42 at => time,
79             };
80 3         20 return $state;
81             }
82              
83             sub try_acquire {
84 397     397 1 1671 my ($self, $id, $key, $quantity) = @_;
85             croak sprintf '%s already acquired %s', $id, $key
86 397 100 66     1047 if $self->{acquired}{$id} && exists $self->{acquired}{$id}{$key};
87 392 100       771 croak 'quantity must be positive' if $quantity <= 0;
88              
89 376   100     1094 my $used = $self->{used}{$key} || 0;
90 376 100       739 if ($used + $quantity > $self->{limit}) {
91 165         453 return;
92             }
93 211         359 $self->{used}{$key} = $used + $quantity;
94              
95 211         399 $self->{acquired}{$id}{$key} = $quantity;
96 211         991 return 1;
97             }
98              
99             sub _release {
100 121     121   178 my ($self, $id) = @_;
101 121 100       417 croak sprintf '%s not acquired anything', $id if !$self->{acquired}{$id};
102              
103 116         119 for my $key (keys %{ $self->{acquired}{$id} }) {
  116         520  
104 130         261 my $quantity = $self->{acquired}{$id}{$key};
105 130         274 $self->{used}{$key} -= $quantity;
106             # clean up (avoid memory leak in long run with unique keys)
107 130 100       275 if (!$self->{used}{$key}) {
108 109         237 delete $self->{used}{$key};
109             }
110             }
111 116         296 delete $self->{acquired}{$id};
112 116         317 throttle_flush();
113 116         503 return $self;
114             }
115              
116              
117             1; # Magic true value required at end of module
118             __END__