File Coverage

blib/lib/Schedule/Poll.pm
Criterion Covered Total %
statement 93 95 97.8
branch 14 20 70.0
condition n/a
subroutine 9 9 100.0
pod 2 3 66.6
total 118 127 92.9


line stmt bran cond sub pod time code
1             package Schedule::Poll;
2              
3 3     3   87467 use 5.006;
  3         13  
  3         182  
4 3     3   21 use strict;
  3         7  
  3         129  
5 3     3   17 use warnings FATAL => 'all';
  3         18  
  3         181  
6              
7 3     3   17 use List::Util 'max';
  3         6  
  3         389  
8 3     3   2864 use POSIX qw/ floor /;
  3         25759  
  3         19  
9 3     3   8044 use Carp;
  3         6  
  3         2562  
10              
11             =head1 NAME
12              
13             Schedule::Poll - Evenly schedule recurring events with various intervals
14              
15              
16             =cut
17              
18             our $VERSION = '0.02';
19              
20              
21             =head1 SYNOPSIS
22              
23              
24             use Schedule::Poll;
25              
26             # Let's run a few things every 3 seconds,
27             # and some things every 6 seconds
28              
29             my $config = {
30             foo => 3,
31             bar => 3,
32             baz => 3,
33             zip => 6,
34             zoo => 6,
35             zat => 6
36             };
37              
38             my $poll = Schedule::Poll->new( $config );
39              
40             while(1) {
41              
42             if (my $aref = $poll->which ) {
43              
44             for my $each (@$aref) {
45              
46             print "$each fired!\n";
47             }
48             }
49             sleep 1;
50             }
51              
52              
53             =head1 METHODS
54              
55             =head2 new
56              
57             Constructor. Accepts a hashref with the values being an interval in seconds. Each interval used should be a divisor of 86400.
58              
59              
60             my $poll = Schedule::Poll->new({
61            
62             foo => 300 # 5 minutes
63             bar => 600 # 10 minutes
64             baz => 5 # 5 seconds
65            
66             });
67              
68             =cut
69              
70             sub new {
71 3     3 1 37 my $class = shift;
72 3         8 my $self = { };
73 3         10 $self->{config} = $_[0];
74 3 50       14 croak "Missing data" unless exists $self->{config};
75 3 50       14 croak "data is not a hashref" unless ref $self->{config} eq 'HASH';
76              
77 3         5 my $config = $self->{config};
78 3         7 my @intervals;
79              
80 3         6 for my $interval (keys %{$config}) {
  3         16  
81 9 50       32 croak "$config->{$interval} is not an even divisor of 86400" if (86400 % $config->{$interval} != 0);
82 9         30 push (@intervals,$config->{$interval});
83             }
84 3         34 my $max = max(@intervals);
85 3         9 $self->{max} = $max;
86              
87 3         8 my %groups = ( );
88 3         7 for my $each (keys %{$config}) {
  3         10  
89 9         11 push ( @{$groups{ $config->{$each} } }, $each);
  9         35  
90             }
91 3         9 my %schedule;
92 3         8 undef @intervals;
93 3         8 undef $config;
94 3         32 undef $self->{config};
95              
96              
97 3         12 for my $interval (keys %groups) {
98             # Count of members in each group:
99 5         8 my $members = scalar @{$groups{$interval}};
  5         11  
100              
101 5         10 my $iter = 1;
102              
103 5 100       24 if ($members/$interval >= 1) {
104              
105             # > 1 req per second. Loop
106             # through the members and assign
107             # them to slots in the interval
108             #
109             # Ex:
110             # members = 7
111             # interval = 5
112             #
113             # 1 2 3 4 5
114             # ---------
115             # | | | | |
116             # | |
117             #
118             # With the above example, the 1st and 2nd second slots in that interval
119             # will contain 2 requests, the remaining slots will have 1
120              
121              
122             # First, We need to determine how many times this
123             # interval group will repeat given a max interval.
124              
125 1         3 my $sets = $max/$interval;
126              
127 1         4 while ($members >= 1) {
128 4 100       10 $iter = 1 if $iter > $interval;
129            
130 4         5 my $set = 0;
131              
132 4         11 while ($set < $sets) {
133              
134 8         8 my $slot;
135 8 100       17 if ($set == 0) {
136 4         7 $slot = $iter;
137             }
138             else {
139 4         24 $slot = ($set * $interval) + $iter;
140             }
141 8         9 push ( @{$schedule{$slot}}, $groups{$interval}[$members -1]);
  8         24  
142 8         22 $set++;
143             }
144              
145 4         3 $iter++;
146 4         13 $members--;
147             }
148             }
149             else {
150              
151             # < 1 requests per sec. We can spread
152             # the reqests out over multiple seconds
153             #
154             # Ex:
155             # requests: 3
156             # interval: 9
157             #
158             # 1 2 3 4 5 6 7 8 9
159             # -----------------
160             # | | |
161             #
162             # .. so 1 request for every 3 seconds.
163              
164 4         41 my $rate = floor 1/($members/$interval);
165              
166              
167 4         9 my $sets = $max/$interval;
168 4         15 while ($members >=1) {
169 5 50       14 $iter = 1 if $iter >= $interval;
170 5         8 my $set = 0;
171 5         17 while ($set < $sets) {
172 124         154 my $slot;
173 124 100       251 if ($set == 0) {
174 5         10 $slot = $iter + ($rate -1);
175              
176             }
177             else {
178 119         213 $slot = ($set * $interval) + ($iter + ($rate-1));
179             }
180 124         157 push ( @{$schedule{$slot}}, $groups{$interval}[$members -1]);
  124         472  
181 124         355 $set++;
182             }
183 5         7 $iter += $rate;
184 5         22 $members--;
185             }
186              
187             }
188             }
189 3         12 $self->{schedule} = \%schedule;
190 3         28 bless $self,$class;
191             };
192              
193              
194             sub current {
195 6     6 0 16 my $self = shift;
196              
197 6         31 my $max = $self->{max};
198 6         540 my @time = localtime();
199 6         36 my $second = ($time[2] * 3600) + ($time[1] * 60) + $time[0];
200 6         64 my $y=0;
201 6 50       30 if ($second > $max) {
202 6         24 while ($y < $second) {
203 25890         56245 $y += $max;
204             }
205 6         70 return $second - ($y - $max);
206             } else {
207 0         0 return $second;
208             }
209              
210             }
211              
212             =head2 which
213              
214             Returns an arrary reference containing the items for that current tick interval.
215              
216             $poll->which;
217              
218              
219             =cut
220              
221             sub which {
222 6     6 1 40 my $self = shift;
223 6         30 my $current = $self->current;
224 6         15 my @who;
225 6 50       56 if (exists $self->{schedule}{$current}) {
226 6         12 for my $each (@{$self->{schedule}{$current}}) {
  6         32  
227 10         59 push(@who,$each);
228             }
229 6         391 return \@who;
230             }
231 0           return 0;
232             }
233              
234             =head2 Examples
235              
236             $href = {
237             a => 3,
238             b => 3,
239             c => 3
240             };
241              
242             Timeline:
243             interval | 1 2 3 4 5 6
244             ---------+------------------
245             key | a b c a b c
246              
247              
248             $href = {
249             a => 3,
250             b => 3,
251             c => 3,
252             d => 6,
253             e => 6,
254             f => 6
255             };
256              
257             Timeline:
258             interval | 1 2 3 4 5 6 7 8 9 10 11 12
259             ---------+--------------------------------------
260             key | b a c b a c b a c b a c
261             | d e f d e f
262              
263              
264             =head1 AUTHOR
265              
266             Michael Kroher, C<< >>
267              
268             =cut
269             1; # End of Schedule::Poll