3 use lib
'/usr/lib/multithrottler';
8 Getopt
::Long
::Configure
('no_ignore_case');
9 no if ($] >= 5.018), 'warnings' => 'experimental::smartmatch';
14 if(defined and $_ ne '')
16 $_ .= "\n" unless /\n$/;
17 s/^/multithrottler: /mg;
23 print STDERR warnxf
(@_);
31 $OverLimitStatusCode = 11;
34 multithrottler [--rate <MAXITEMS>/<INTERVAL> | --reset] --zone <FILE>
35 multithrottler [-r RATE | -Z] --zone <FILE> [-F] [-c CODE] [--item <KEY>] <COMMAND> [<ARGS>]
36 multithrottler [--info] [--dump] --zone <FILE>
39 -r, --rate define how many items are permitted within a given interval
40 (in seconds). you can just simply define/redefine rate or
41 define/redefine rate and run command in a single invocation.
42 not need to define rate if you already have the zone file.
43 if you redefine the rate, counters get reset.
44 -n, --no-redefine-rate
45 --no-redefine define rate only if the zone does not exists yet,
46 i.e. prevent overwrite. use with --rate.
48 --zone, --file where to store throttling records
49 -Z, --reset reset counters in the zone
50 -F, --force force insert item. but throttle is neccessary.
51 -c, --code status code for throttling (default is $OverLimitStatusCode)
53 --item, --key key of the item to insert (default is \".\")
54 --info display rate info
55 --dump dump zone records
57 %ThrottlerParams = ('backend' => 'YAML');
63 my $zonedata = YAML
::LoadFile
($Zonefile);
64 my $rate = $zonedata->{'chain'}->{'max_items'} . '/' . $zonedata->{'chain'}->{'interval'};
65 parseRate
($rate) or die "Can not parse rate info";
67 } or errx
"Could not load YAML '$Zonefile'. Delete and create again.\n$@";
72 if($Rate =~ /^(\d+)\/(\d
+)$/) {
73 $ThrottlerParams{'max_items'} = $1;
74 $ThrottlerParams{'interval'} = $2;
82 $Throttler = Throttler
->new(%ThrottlerParams) or errx
"Could not cast Throttler instance";
87 'r|rate=s' => sub{ parseRate
($_[1]) or die; },
88 'n|no-redefine|no-redefine-rate' => \
$noRedefineRate,
89 'Z|reset' => \
$ThrottlerParams{'reset'},
90 'F|force' => \
$doForce,
91 'f|z|file|zone|zonefile=s' => \
$Zonefile,
92 'i|k|item|key|itemkey=s' => \
$Itemkey,
93 'c|code=i' => \
$OverLimitStatusCode,
99 $ThrottlerParams{'backend_options'}->{'db_file'} = $Zonefile;
100 $doReset = $ThrottlerParams{'reset'};
101 ($Command, @Args) = @ARGV;
105 if(defined $Zonefile)
108 $mode .= 'r' if defined $Rate;
109 $mode .= 'Z' if $doReset;
110 $mode .= 'C' if defined $Command;
111 $mode .= 'F' if $doForce;
112 $mode .= 'x' if $doInfo or $doDump;
114 if(not($mode ~~ ['r', 'rC', 'rCF', 'Z', 'C', 'CF', 'x', 'ZC', 'ZCF']))
116 errx
"Wrong combination of options. ($mode)";
125 print "rate=$Rate\n";
130 print $Throttler->buckets_dump;
137 eval { readRate
; 1; }
150 for my $attempt (1, 2)
153 if($Throttler->try_push('key' => $Itemkey, 'force' => $doForce))
168 warnx
"Throttler module error\n$@";
171 warnx
"Resetting Throttler zone file";
172 # if there was Throttler module exception, try to reset the backend repairing it so.
174 $ThrottlerParams{'reset'} = 1;
186 exec {$Command} $Command, @Args;
187 my ($errno, $errstr) = (int $!, $!);
188 warn "$0: $Command: $errstr\n";
193 exit $OverLimitStatusCode;
198 # it was either just a rate setup or counter reset
212 multithrottler - Run given command if not reached the defined rate limit