File Coverage

blib/lib/IO/Iron/Applications/IronCache/Functionality.pm
Criterion Covered Total %
statement 47 463 10.1
branch 0 108 0.0
condition 0 21 0.0
subroutine 17 53 32.0
pod n/a
total 64 645 9.9


line stmt bran cond sub pod time code
1             package IO::Iron::Applications::IronCache::Functionality;
2              
3 1     1   25444 use 5.010_000;
  1         4  
  1         46  
4 1     1   8 use strict;
  1         1  
  1         53  
5 1     1   6 use warnings FATAL => 'all';
  1         6  
  1         46  
6 1     1   811 use English;
  1         5561  
  1         7  
7              
8             # Global creator
9 1     1   821 BEGIN {
10             # No exports
11             }
12              
13             # Global destructor
14 1     1   210 END {
15             }
16              
17             # ABSTRACT: ironcache.pl command internals: functionality.
18              
19             our $VERSION = '0.12'; # VERSION: generated by DZP::OurPkgVersion
20              
21 1     1   1088 use Log::Any qw{$log};
  1         2198  
  1         4  
22 1     1   1010 use Params::Validate qw(:all);
  1         12177  
  1         311  
23 1     1   10 use Scalar::Util qw(looks_like_number);
  1         3  
  1         88  
24 1     1   873 use HTTP::Status qw(:constants :is status_message);
  1         4901  
  1         690  
25 1     1   12 use Carp;
  1         2  
  1         74  
26 1     1   5 use Try::Tiny;
  1         3  
  1         68  
27 1     1   6 use Scalar::Util qw{blessed looks_like_number};
  1         2  
  1         60  
28 1     1   966 use Carp::Assert;
  1         1399  
  1         8  
29 1     1   1236 use Carp::Assert::More;
  1         2612  
  1         240  
30 1     1   926 use Parallel::Loops;
  1         53923  
  1         67  
31              
32             require IO::Iron::IronCache::Client;
33             require IO::Iron::IronCache::Cache;
34             require IO::Iron::IronCache::Item;
35              
36             use constant {
37 1         6865 OPERATION_PUT => q{put},
38             OPERATION_INCREMENT => q{increment},
39             OPERATION_DELETE => q{delete},
40 1     1   10 };
  1         3  
41              
42              
43             sub list_caches {
44 0     0     my %params = validate(
45             @_, {
46             'config' => { type => SCALAR, optional => 1, }, # config file name.
47             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
48             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
49             'alternatives' => { type => BOOLEAN, optional => 1, }, # only show alternative cache names and item keys.
50             }
51             );
52 0           $log->tracef('Entering list_caches(%s)', \%params);
53              
54 0           my %cache_params;
55 0 0         $cache_params{'config'} = $params{'config'} if defined $params{'config'};
56 0 0         $cache_params{'policies'} = $params{'policies'} if defined $params{'policies'};
57 0           my $client = IO::Iron::IronCache::Client->new(%cache_params);
58 0           my %output = ( 'project_id' => $client->project_id());
59 0           my @caches = $client->get_caches();
60 0           my %infos;
61 0           foreach my $cache (@caches) {
62 0           my %info;
63 0           $info{'name'} = $cache->name();
64 0           $infos{$cache->name()} = \%info;
65             }
66 0           $output{'caches'} = \%infos;
67 0           $log->tracef('Exiting list_caches()');
68 0           return %output;
69             }
70              
71              
72             sub list_items {
73 0     0     my %params = validate(
74             @_, {
75             'config' => { type => SCALAR, optional => 1, }, # config file name.
76             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
77             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
78             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache name (or string with wildcards?).
79             'item_key' => { type => ARRAYREF, optional => 0, }, # item key.
80             'alternatives' => { type => BOOLEAN, optional => 1, }, # only show alternative cache names and item keys.
81             }
82             );
83 0           $log->tracef('Entering list_items(%s)', \%params);
84              
85 0           my %output;
86 0 0         if($params{'alternatives'}) {
87 0           my $client = _prepare_client(%params);
88 0 0         if($client->is_item_key_alternatives()) {
89 0           _expand_item_keys('pointer_to_params' => \%params, 'client' => $client);
90             }
91             else {
92 0           $log->warnf('No limiting policy used. Cannot print alternatives.');
93             }
94 0 0         if($client->is_cache_name_alternatives()) {
95 0           _expand_cache_names('pointer_to_params' => \%params, 'client' => $client);
96             }
97             else {
98 0           $log->warnf('No limiting policy used. Cannot print alternatives.');
99             }
100 0           my %items_and_caches = _prepare_items_and_caches(%params);
101 0           my @sorted_keys = sort { $items_and_caches{$a}->{'order'} <=> $items_and_caches{$b}->{'order'}} keys %items_and_caches;
  0            
102 0           foreach my $sorted_key (@sorted_keys) {
103 0           my ($cache_name, $item_key) = split '/', $sorted_key;
104 0           my %result = ( 'error' => 'Not queried');
105 0           $output{'caches'}->{$cache_name}->{'items'}->{$item_key} = \%result;
106             }
107             }
108             else {
109 0           my %results;
110 0           my $client = _prepare_client(%params);
111 0 0         if($client->is_item_key_alternatives()) {
112 0           _expand_item_keys('pointer_to_params' => \%params, 'client' => $client);
113             }
114             else {
115 0           $log->warnf('No limiting policy used. Cannot print alternatives.');
116             }
117 0 0         if($client->is_cache_name_alternatives()) {
118 0           _expand_cache_names('pointer_to_params' => \%params, 'client' => $client);
119             }
120             else {
121 0           $log->warnf('No limiting policy used. Cannot print alternatives.');
122             }
123 0           my %items_and_caches = _prepare_items_and_caches(%params);
124 0           $log->debugf('list_items(): items_and_caches=%s', \%items_and_caches);
125 0           my $parallel_exe = Parallel::Loops->new(scalar keys %items_and_caches );
126 0           $parallel_exe->share(\%items_and_caches);
127 0           $parallel_exe->share(\%results);
128 0           my @keys = keys %items_and_caches;
129 0           $log->debugf('list_items(): keys=%s', \@keys);
130             my @rval_results = $parallel_exe->foreach(\@keys, sub {
131 0     0     my $details_to_find_item = $items_and_caches{$_};
132 0           my %result;
133 0           eval {
134 0           %result = _get_item_thread('details_to_find_item' => $details_to_find_item, 'pointer_to_params' => \%params);
135             };
136 0 0         if($EVAL_ERROR) {
137 0           print $EVAL_ERROR;
138 0           return;
139             }
140             else {
141 0           $results{$_} = \%result;
142 0           return 1;
143             }
144 0           });
145            
146 0           $log->debugf('list_items(): All parallel loops processed.');
147 0           $log->debugf('list_items(): results=%s', \%results);
148 0           my @sorted_keys = sort { $items_and_caches{$a}->{'order'} <=> $items_and_caches{$b}->{'order'}} keys %items_and_caches;
  0            
149 0           foreach my $sorted_key (@sorted_keys) {
150 0           my ($cache_name, $item_key) = split '/', $sorted_key;
151 0           $output{'caches'}->{$cache_name}->{'items'}->{$item_key} = $results{$sorted_key};
152             }
153             }
154 0           $log->tracef('Exiting list_items():%s', \%output);
155 0           return %output;
156             }
157              
158             sub _expand_cache_names {
159 0     0     my %params = validate_with(
160             'params' => \@_, 'spec' => {
161             'pointer_to_params' => { type => HASHREF, optional => 0, }, # item key.
162             'client' => { type => OBJECT, optional => 0, }, # ref to IronCache client.
163             }, 'allow_extra' => 1,
164             );
165 0           $log->tracef('Entering _expand_cache_names(%s)', \%params);
166 0           my @alternatives = $params{'client'}->cache_name_alternatives();
167 0           my @valid_alternatives;
168 0           foreach my $alternative (@alternatives) {
169 0           foreach my $candidate (@{$params{'pointer_to_params'}->{'cache_name'}}) {
  0            
170 0 0         if($alternative =~ /^$candidate$/) {
171 0           push @valid_alternatives, $alternative;
172             }
173             }
174             }
175 0           $params{'pointer_to_params'}->{'cache_name'} = \@valid_alternatives;
176 0           $log->tracef('Exiting _expand_cache_names():%s', '[UNDEF]');
177 0           return;
178             }
179              
180             sub _expand_item_keys {
181 0     0     my %params = validate_with(
182             'params' => \@_, 'spec' => {
183             'pointer_to_params' => { type => HASHREF, optional => 0, }, # item key.
184             'client' => { type => OBJECT, optional => 0, }, # ref to IronCache client.
185             }, 'allow_extra' => 1,
186             );
187 0           $log->tracef('Entering _expand_item_keys(%s)', \%params);
188 0           my @alternatives = $params{'client'}->item_key_alternatives();
189 0           my @valid_alternatives;
190 0           foreach my $alternative (@alternatives) {
191 0           foreach my $candidate (@{$params{'pointer_to_params'}->{'item_key'}}) {
  0            
192 0 0         if($alternative =~ /$candidate/) {
193 0           push @valid_alternatives, $alternative;
194             }
195             }
196             }
197 0           $params{'pointer_to_params'}->{'item_key'} = \@valid_alternatives;
198 0           $log->tracef('Exiting _expand_item_keys():%s', \@valid_alternatives);
199 0           return @valid_alternatives;
200             }
201              
202              
203             sub show_cache {
204 0     0     my %params = validate(
205             @_, {
206             'config' => { type => SCALAR, optional => 1, }, # config file name.
207             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
208             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
209             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache names in array
210             }
211             );
212 0           $log->tracef('Entering show_cache(%s)', \%params);
213              
214 0           my %cache_params;
215 0 0         $cache_params{'config'} = $params{'config'} if defined $params{'config'};
216 0 0         $cache_params{'policies'} = $params{'policies'} if defined $params{'policies'};
217 0           my $client = IO::Iron::IronCache::Client->new(%cache_params);
218 0           my %output = ( 'project_id' => $client->project_id());
219 0           my @cache_infos;
220 0           foreach my $cache_name (@{$params{'cache_name'}}) {
  0            
221 0           my $cache_info = $client->get_info_about_cache('name' => $cache_name);
222 0           $log->debugf("show_cache(): Fetched info about cache:%s", $cache_info);
223 0           push @cache_infos, $cache_info;
224             }
225 0           my %infos;
226 0           foreach my $info (@cache_infos) {
227 0           my %info;
228 0           $info{'name'} = $info->{'name'};
229 0           $info{'id'} = $info->{'id'};
230 0           $info{'project_id'} = $info->{'project_id'};
231 0           $info{'size'} = $info->{'size'};
232 0 0         $info{'data_size'} = $info->{'data_size'} if defined $info->{'data_size'};
233 0 0         $info{'created_at'} = $info->{'created_at'} if defined $info->{'created_at'};
234 0 0         $info{'updated_at'} = $info->{'updated_at'} if defined $info->{'updated_at'};
235 0           $infos{$info->{'name'}} = \%info;
236             }
237 0           $output{'caches'} = \%infos;
238 0           $log->tracef('Exiting show_cache()');
239 0           return %output;
240             }
241              
242              
243             sub clear_cache {
244 0     0     my %params = validate(
245             @_, {
246             'config' => { type => SCALAR, optional => 1, }, # config file name.
247             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
248             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
249             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache names in array
250             }
251             );
252 0           $log->tracef('Entering clear_cache(%s)', \%params);
253              
254 0           my %cache_params;
255 0 0         $cache_params{'config'} = $params{'config'} if defined $params{'config'};
256 0 0         $cache_params{'policies'} = $params{'policies'} if defined $params{'policies'};
257 0           my $client = IO::Iron::IronCache::Client->new(%cache_params);
258 0           my %output = ( 'project_id' => $client->project_id());
259 0           foreach my $cache_name (@{$params{'cache_name'}}) {
  0            
260 0           my $cache = $client->get_cache('name' => $cache_name);
261 0           $cache->clear();
262             }
263 0           $log->tracef('Exiting clear_cache()');
264 0           return %output;
265             }
266              
267              
268             sub delete_cache {
269 0     0     my %params = validate(
270             @_, {
271             'config' => { type => SCALAR, optional => 1, }, # config file name.
272             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
273             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
274             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache names in array
275             }
276             );
277 0           $log->tracef('Entering delete_cache(%s)', \%params);
278              
279 0           my %cache_params;
280 0 0         $cache_params{'config'} = $params{'config'} if defined $params{'config'};
281 0 0         $cache_params{'policies'} = $params{'policies'} if defined $params{'policies'};
282 0           my $client = IO::Iron::IronCache::Client->new(%cache_params);
283 0           my %output = ( 'project_id' => $client->project_id());
284 0           foreach my $cache_name (@{$params{'cache_name'}}) {
  0            
285 0           $client->delete_cache('name' => $cache_name);
286             }
287 0           $log->tracef('Exiting _elete_cache()');
288 0           return %output;
289             }
290              
291             sub _put_item_thread {
292 0     0     my %params = validate(
293             @_, {
294             'item_info' => { type => HASHREF, optional => 0, },
295             'pointer_to_params' => { type => HASHREF, optional => 0, }, # item key.
296             }
297             );
298 0           $log->tracef('Entering _put_item_thread(%s)', \%params);
299              
300 0           my %result;
301 0           my $cache_name = $params{'item_info'}->{'cache_name'};
302 0           my $item_key = $params{'item_info'}->{'item_key'};
303 0           $log->debugf('_put_item_thread():cache_name=%s;item_key=%s;', $cache_name, $item_key);
304 0           my $client = _prepare_client(%{$params{'pointer_to_params'}});
  0            
305 0           my $cache = _get_cache_safely('client' => $client, 'cache_name' => $cache_name);
306 0 0 0       if (!$cache && $params{'item_info'}->{'create_cache'}) {
307 0           $log->infof('Cache \'%s\' does not exist. Creating new cache.', $cache_name);
308 0           $cache = $client->create_cache('name' => $cache_name);
309             }
310 0 0         if($cache) {
311 0           $log->debugf("put_item(): To item: \'%s\'.'.", $item_key);
312 0           my %item_parameters;
313 0           $item_parameters{'value'} = $params{'item_info'}->{'item_value'};
314 0 0         $item_parameters{'expires_in'} = $params{'item_info'}->{'expires_in'}
315             if $params{'item_info'}->{'expires_in'};
316 0           my $item = IO::Iron::IronCache::Item->new(%item_parameters);
317 0           my $rval = _operate_item_safely('cache' => $cache,
318             'item_key' => $item_key, 'operation' => OPERATION_PUT,
319             'item' => $item);
320 0 0         if($rval) {
321             # Attn. _operate_item_safely returns undef if alright, otherwise error message.
322 0           $result{'error'} = $rval;
323             }
324             }
325             else {
326 0           $log->warnf('Cache \'%s\' does not exist. Skip item get. (use option --create-cache to insert to a new cache.) ...', $cache_name);
327 0           $result{'error'} = 'Cache not exists.';
328             } # if cache else
329 0           return %result;
330             }
331              
332              
333             sub put_item {
334 0     0     my %params = validate(
335             @_, {
336             'config' => { type => SCALAR, optional => 1, }, # config file name.
337             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
338             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
339             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache name (or string with wildcards?).
340             'item_key' => { type => ARRAYREF, optional => 0, }, # item key.
341             'item_value' => { type => SCALAR, optional => 0, }, # item value.
342             'create_cache' => { type => BOOLEAN, optional => 1, }, # create cache if cache does not exist.
343             'expires_in' => { type => SCALAR, optional => 1, }, # item expires in ? seconds.
344             }
345             );
346 0           $log->tracef('Entering put_item(%s)', \%params);
347              
348 0           my %output;
349             my %results;
350 0           my %items_and_caches = _prepare_items_and_caches(%params);
351 0           $log->debugf('put_item(): items_and_caches=%s', \%items_and_caches);
352 0           my $parallel_exe = Parallel::Loops->new(scalar keys %items_and_caches );
353 0           $parallel_exe->share(\%items_and_caches);
354 0           $parallel_exe->share(\%results);
355 0           my @keys = keys %items_and_caches;
356 0           $log->debugf('put_item(): keys=%s', \@keys);
357             my @rval_results = $parallel_exe->foreach(\@keys, sub {
358 0     0     my $key = $_;
359 0           my $value = $items_and_caches{$key};
360 0           my %result;
361 0           eval {
362 0           %result = _put_item_thread('item_info' => $value, 'pointer_to_params' => \%params);
363             };
364 0 0         if($EVAL_ERROR) {
365 0           print $EVAL_ERROR;
366 0           return;
367             }
368             else {
369 0           $results{$key} = \%result;
370 0           return 1;
371             }
372 0           });
373              
374 0           $log->debugf('put_item(): All parallel loops processed.');
375 0           $log->debugf('put_item(): results=%s', \%results);
376 0           my @sorted_keys = sort { $items_and_caches{$a}->{'order'} <=> $items_and_caches{$b}->{'order'}} keys %items_and_caches;
  0            
377 0           foreach my $sorted_key (@sorted_keys) {
378 0 0         if(exists $results{$sorted_key}->{'error'}) {
379 0           print $results{$sorted_key}->{'error'} . "\n";
380             }
381             }
382 0           $log->tracef('Exiting put_item():%s', \%output);
383 0           return %output;
384             }
385              
386             sub _increment_item_thread {
387 0     0     my %params = validate(
388             @_, {
389             'item_info' => { type => HASHREF, optional => 0, },
390             'pointer_to_params' => { type => HASHREF, optional => 0, }, # item key.
391             }
392             );
393 0           $log->tracef('Entering _increment_item_thread(%s)', \%params);
394              
395 0           my %result;
396 0           my $cache_name = $params{'item_info'}->{'cache_name'};
397 0           my $item_key = $params{'item_info'}->{'item_key'};
398 0           $log->debugf('_increment_item_thread():cache_name=%s;item_key=%s;', $cache_name, $item_key);
399 0           my $client = _prepare_client(%{$params{'pointer_to_params'}});
  0            
400 0           my $cache = _get_cache_safely('client' => $client, 'cache_name' => $cache_name);
401 0 0 0       if (!$cache && $params{'item_info'}->{'create_cache'}) {
402 0           $log->infof('Cache \'%s\' does not exist. Creating new cache.', $cache_name);
403 0           $cache = $client->create_cache('name' => $cache_name);
404             }
405 0 0         if($cache) {
406 0           $log->debugf("_increment_item_thread(): To item: \'%s\'.'.", $item_key);
407 0           my $rval = _operate_item_safely('cache' => $cache,
408             'item_key' => $item_key, 'operation' => OPERATION_INCREMENT,
409             'increment' =>$params{'item_info'}->{'item_increment'});
410 0 0         if($rval) {
411             # Attn. _operate_item_safely returns undef if alright, otherwise error message.
412 0           $result{'error'} = $rval;
413             }
414             }
415             else {
416 0           $log->warnf('Cache \'%s\' does not exist. Skip item get ...', $cache_name);
417 0           $result{'error'} = 'Cache not exists.';
418             }
419 0           return %result;
420             }
421              
422              
423             sub increment_item {
424 0     0     my %params = validate(
425             @_, {
426             _common_arguments(),
427             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache names (can be one).
428             'item_key' => { type => ARRAYREF, optional => 0, }, # item keys (can be one).
429             'item_increment' => { type => SCALAR, optional => 0, }, # increment item by this value.
430             'create_cache' => { type => BOOLEAN, optional => 0, }, # create cache if cache does not exist.
431             }
432             );
433 0           $log->tracef('Entering increment_item(%s)', \%params);
434              
435 0           my %output;
436             my %results;
437 0           my %items_and_caches = _prepare_items_and_caches(%params);
438 0           $log->debugf('put_item(): items_and_caches=%s', \%items_and_caches);
439 0           my $parallel_exe = Parallel::Loops->new(scalar keys %items_and_caches );
440 0           $parallel_exe->share(\%results);
441 0           my @keys = keys %items_and_caches;
442 0           $log->debugf('put_item(): keys=%s', \@keys);
443             my @rval_results = $parallel_exe->foreach(\@keys, sub {
444 0     0     my $key = $_;
445 0           my $value = $items_and_caches{$key};
446 0           my %result;
447 0           eval {
448 0           %result = _increment_item_thread('item_info' => $value, 'pointer_to_params' => \%params);
449             };
450 0 0         if($EVAL_ERROR) {
451 0           print $EVAL_ERROR;
452 0           return;
453             }
454             else {
455 0           $results{$key} = \%result;
456 0           return 1;
457             }
458 0           });
459              
460 0           $log->debugf('increment_item(): All parallel loops processed.');
461 0           $log->debugf('increment_item(): results=%s', \%results);
462 0           my @sorted_keys = sort { $items_and_caches{$a}->{'order'} <=> $items_and_caches{$b}->{'order'}} keys %items_and_caches;
  0            
463 0           foreach my $sorted_key (@sorted_keys) {
464 0 0         if(exists $results{$sorted_key}->{'error'}) {
465 0           print $results{$sorted_key}->{'error'} . "\n";
466             }
467             }
468 0           $log->tracef('Exiting increment_item():%s', \%output);
469 0           return %output;
470             }
471              
472             sub _get_item_thread {
473 0     0     my %params = validate(
474             @_, {
475             'details_to_find_item' => { type => HASHREF, optional => 0, },
476             'pointer_to_params' => { type => HASHREF, optional => 0, }, # item key.
477             }
478             );
479 0           $log->tracef('Entering _get_item_thread(%s)', \%params);
480              
481 0           my %result;
482 0           my $cache_name = $params{'details_to_find_item'}->{'cache_name'};
483 0           my $item_key = $params{'details_to_find_item'}->{'item_key'};
484 0           $log->debugf('_get_item_thread():cache_name=%s;item_key=%s;', $cache_name, $item_key);
485 0           my $client = _prepare_client(%{$params{'pointer_to_params'}});
  0            
486 0           my $cache = _get_cache_safely('client' => $client, 'cache_name' => $cache_name);
487 0 0         if($cache) {
488 0           my $item = _get_item_safely('cache' => $cache, 'item_key' => $item_key);
489 0 0         if($item) {
490 0           $log->debugf("_get_item_thread(): Finished getting item \'%s\' from cache \'%s\'.", $item_key, $cache_name);
491 0           $result{'value'} = $item->value();
492 0           $result{'cas'} = $item->cas();
493 0 0         $result{'expires'} = $item->expires() if $item->expires();
494             }
495             else {
496 0           $log->warnf('Item \'%s\' does not exist in cache \'%s\'.', $item_key, $cache_name);
497 0           $result{'error'} = 'Key not exists.';
498             }
499             }
500             else {
501 0           $log->warnf('Cache \'%s\' does not exist. Skip item get ...', $cache_name);
502 0           $result{'error'} = 'Cache not exists.';
503             } # if cache else
504 0           return %result;
505             }
506              
507              
508             sub get_item {
509 0     0     my %params = validate(
510             @_, {
511             'config' => { type => SCALAR, optional => 1, }, # config file name.
512             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
513             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
514             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache name (or string with wildcards?).
515             'item_key' => { type => ARRAYREF, optional => 0, }, # item key.
516             }
517             );
518 0           $log->tracef('Entering get_item(%s)', \%params);
519              
520 0           my %output;
521             my %results;
522 0           my %items_and_caches = _prepare_items_and_caches(%params);
523 0           $log->debugf('get_item(): items_and_caches=%s', \%items_and_caches);
524 0           my $parallel_exe = Parallel::Loops->new(scalar keys %items_and_caches );
525 0           $parallel_exe->share(\%results);
526 0           my @keys = keys %items_and_caches;
527 0           $log->debugf('get_item(): keys=%s', \@keys);
528             my @rval_results = $parallel_exe->foreach(\@keys, sub {
529 0     0     my $key = $_;
530 0           my $value = $items_and_caches{$key};
531 0           my %result;
532 0           eval {
533 0           %result = _get_item_thread('details_to_find_item' => $value, 'pointer_to_params' => \%params);
534             };
535 0 0         if($EVAL_ERROR) {
536 0           print $EVAL_ERROR;
537 0           return;
538             }
539             else {
540 0           $results{$key} = \%result;
541 0           return 1;
542             }
543 0           });
544              
545 0           $log->debugf('get_item(): All parallel loops processed.');
546 0           $log->debugf('get_item(): results=%s', \%results);
547 0           my @sorted_keys = sort { $items_and_caches{$a}->{'order'} <=> $items_and_caches{$b}->{'order'}} keys %items_and_caches;
  0            
548 0           foreach my $sorted_key (@sorted_keys) {
549 0 0         if(exists $results{$sorted_key}->{'value'}) {
550 0           print $results{$sorted_key}->{'value'} . "\n";
551             }
552             else {
553 0           print $results{$sorted_key}->{'error'} . "\n";
554             }
555             }
556 0           $log->tracef('Exiting get_item():%s', \%output);
557 0           return %output;
558             }
559              
560             #
561             # Delete item
562             #
563              
564             sub _delete_item_thread {
565 0     0     my %params = validate(
566             @_, {
567             'item_info' => { type => HASHREF, optional => 0, },
568             'pointer_to_params' => { type => HASHREF, optional => 0, }, # item key.
569             }
570             );
571 0           $log->tracef('Entering _delete_item_thread(%s)', \%params);
572              
573 0           my %result;
574 0           my $cache_name = $params{'item_info'}->{'cache_name'};
575 0           my $item_key = $params{'item_info'}->{'item_key'};
576 0           $log->debugf('_delete_item_thread():cache_name=%s;item_key=%s;', $cache_name, $item_key);
577 0           my $client = _prepare_client(%{$params{'pointer_to_params'}});
  0            
578 0           my $cache = _get_cache_safely('client' => $client, 'cache_name' => $cache_name);
579 0 0         if($cache) {
580 0           my $rval = _operate_item_safely('cache' => $cache,
581             'item_key' => $item_key, 'operation' => OPERATION_DELETE, );
582 0 0         if($rval) {
583             # Attn. _operate_item_safely returns undef if alright, otherwise error message.
584 0           $result{'error'} = $rval;
585             }
586             }
587             else {
588 0           $log->warnf('Cache \'%s\' does not exist. Skip item get ...', $cache_name);
589 0           $result{'error'} = 'Cache not exists.';
590             } # if cache else
591 0           return %result;
592             }
593              
594              
595             sub delete_item {
596 0     0     my %params = validate(
597             @_, {
598             'config' => { type => SCALAR, optional => 1, }, # config file name.
599             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
600             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
601             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache name (or string with wildcards?).
602             'item_key' => { type => ARRAYREF, optional => 0, }, # item key.
603             }
604             );
605 0           $log->tracef('Entering delete_item(%s)', \%params);
606              
607 0           my %output;
608             my %results;
609 0           my %items_and_caches = _prepare_items_and_caches(%params);
610 0           $log->debugf('delete_item(): items_and_caches=%s', \%items_and_caches);
611 0           my $parallel_exe = Parallel::Loops->new(scalar keys %items_and_caches );
612 0           $parallel_exe->share(\%items_and_caches);
613 0           $parallel_exe->share(\%results);
614 0           my @keys = keys %items_and_caches;
615 0           $log->debugf('delete_item(): keys=%s', \@keys);
616             my @rval_results = $parallel_exe->foreach(\@keys, sub {
617 0     0     my $key = $_;
618 0           my $value = $items_and_caches{$key};
619 0           my %result;
620 0           eval {
621 0           %result = _delete_item_thread('item_info' => $value, 'pointer_to_params' => \%params);
622             };
623 0 0         if($EVAL_ERROR) {
624 0           print $EVAL_ERROR;
625 0           return;
626             }
627             else {
628 0           $results{$key} = \%result;
629 0           return 1;
630             }
631 0           });
632              
633 0           $log->debugf('delete_item(): All parallel loops processed.');
634 0           $log->debugf('delete_item(): results=%s', \%results);
635 0           my @sorted_keys = sort { $items_and_caches{$a}->{'order'} <=> $items_and_caches{$b}->{'order'}} keys %items_and_caches;
  0            
636 0           foreach my $sorted_key (@sorted_keys) {
637 0 0         if(exists $results{$sorted_key}->{'error'}) {
638 0           print $results{$sorted_key}->{'error'} . "\n";
639             }
640             }
641 0           $log->tracef('Exiting delete_item():%s', \%output);
642 0           return %output;
643             }
644              
645             ### Internals
646              
647             sub _common_arguments {
648             return (
649 0     0     'config' => { type => SCALAR, optional => 1, }, # config file name.
650             'policies' => { type => SCALAR, optional => 1, }, # policy file name.
651             'no-policy' => { type => BOOLEAN, optional => 1, }, # disable all policy checks.
652             );
653             }
654              
655             # Put caches and items in a hash arranged by an ascending number
656             # in a preparation for processing, possibly threading or forking.
657             # Return hash:
658             #%hash = {
659             # '!cache_name/item_key!' => {
660             # 'cache_name' => '!!', 'item_key' => '!!', 'order' => 1, ('value' => "",)
661             # }
662             #};
663              
664             # TODO rename cache_name => names, key => keys
665             sub _prepare_items_and_caches {
666 0     0     my %params = validate_with(
667             'params' => \@_, 'spec' => {
668             'cache_name' => { type => ARRAYREF, optional => 0, }, # cache name (or string with wildcards?).
669             'item_key' => { type => ARRAYREF, optional => 0, }, # item key.
670             'item_value' => { type => SCALAR, optional => 1, }, # item value.
671             'create_cache' => { type => BOOLEAN, optional => 1, }, # create cache if cache does not exist.
672             'expires_in' => { type => SCALAR, optional => 1, }, # item expires in ? seconds.
673             'item_increment' => { type => SCALAR, optional => 1, }, # increment item by this value.
674             }, 'allow_extra' => 1,
675             );
676 0           my %arranged_order;
677 0           my $counter = 1;
678 0           foreach my $cache_name (@{$params{'cache_name'}}) {
  0            
679 0           foreach my $item_key (@{$params{'item_key'}}) {
  0            
680 0           my %item = ( 'cache_name' => $cache_name, 'item_key' => $item_key, 'order' => $counter);
681 0           @item{q{item_value},q{create_cache},q{expires_in},q{item_increment}} =
682             @params{q{item_value},q{create_cache},q{expires_in},q{item_increment}};
683 0           $arranged_order{$cache_name . '/' . $item_key} = \%item;
684 0           $counter++;
685             }
686             }
687 0           return %arranged_order;
688             }
689              
690             sub _prepare_client {
691 0     0     my %params = validate_with(
692             'params' => \@_, 'spec' => {
693             _common_arguments(),
694             }, 'allow_extra' => 1,
695             );
696 0           my %cache_params;
697 0 0         $cache_params{'config'} = $params{'config'} if defined $params{'config'};
698 0 0         $cache_params{'policies'} = $params{'policies'} if defined $params{'policies'};
699 0           return IO::Iron::IronCache::Client->new(%cache_params);
700             }
701              
702             # Return undef if cache with given name does not exist.
703             sub _get_cache_safely {
704 0     0     my %params = validate(
705             @_, {
706             'client' => { type => OBJECT, isa => 'IO::Iron::IronCache::Client', optional => 0, }, # client.
707             'cache_name' => { type => SCALAR, optional => 0, }, # cache name.
708             }
709             );
710 0           $log->tracef("'Entering _get_cache_safely(): %s.'.", \%params);
711              
712 0           my $cache;
713             try {
714 0     0     $cache = $params{'client'}->get_cache('name' => $params{'cache_name'});
715             }
716             catch {
717 0     0     $log->debugf('_get_cache_safely(): Caught exception:%s', $_);
718 0 0 0       croak $_ unless blessed $_ && $_->can('rethrow'); ## no critic (ControlStructures::ProhibitPostfixControls)
719 0 0         if ( $_->isa('IronHTTPCallException') ) {
720 0 0         if( $_->status_code == HTTP_NOT_FOUND ) {
721 0           $log->debugf('_get_cache_safely(): Exception: 404 Cache not found.');
722 0           return;
723             }
724             else {
725 0           $_->rethrow;
726             }
727             }
728             else {
729 0           $_->rethrow;
730             }
731             }
732 0     0     finally {
733 0           };
734              
735 0           $log->tracef("'Exiting _get_cache_safely(): %s.'.", $cache);
736 0           return $cache;
737             }
738              
739             # Return undef if item with given name does not exist.
740             sub _get_item_safely {
741 0     0     my %params = validate(
742             @_, {
743             'cache' => { type => OBJECT, isa => 'IO::Iron::IronCache::Cache', optional => 0, }, # cache.
744             'item_key' => { type => SCALAR, optional => 0, }, # item key.
745             }
746             );
747 0           $log->tracef("'Entering _get_item_safely(): %s.'.", \%params);
748              
749 0           my $item;
750             try {
751 0     0     $item = $params{'cache'}->get('key' => $params{'item_key'});
752             }
753             catch {
754 0     0     $log->debugf('_get_item_safely(): Caught exception:%s', $_);
755 0 0 0       croak $_ unless blessed $_ && $_->can('rethrow'); ## no critic (ControlStructures::ProhibitPostfixControls)
756 0 0         if ( $_->isa('IronHTTPCallException') ) {
757 0 0         if( $_->status_code == HTTP_NOT_FOUND ) {
758 0           $log->debugf('_get_item_safely(): Exception: 404 Key not found.');
759 0           return;
760             }
761             else {
762 0           $_->rethrow;
763             }
764             }
765             else {
766 0           $_->rethrow;
767             }
768             }
769 0     0     finally {
770 0           };
771 0           $log->tracef("'Exiting _get_item_safely(): %s.'.", $item);
772 0           return $item;
773             }
774              
775             # Do put, increment, delete
776             # Return undef if everything alright. Otherwise error string.
777             sub _operate_item_safely {
778             my %params = validate(
779             @_, {
780             'cache' => { type => OBJECT, isa => 'IO::Iron::IronCache::Cache', optional => 0, }, # cache.
781             'item_key' => { type => SCALAR, optional => 0, },
782             'operation' => { type => SCALAR, optional => 0, }, # put|increment|delete
783             'item' => { type => OBJECT, isa => 'IO::Iron::IronCache::Item', optional => 1, }, # IO::Iron::IronCache::Item if operation = put.
784             'increment' => { type => SCALAR, optional => 1, callbacks => {
785 0     0     'Integer check' => sub { return Scalar::Util::looks_like_number(shift); },
786             }}, # SCALAR(int) if operation = increment.
787             }
788 0     0     );
789 0   0       assert(
790             ($params{'operation'} eq OPERATION_PUT && $params{'item'} && !$params{'increment'})
791             || ($params{'operation'} eq OPERATION_INCREMENT && $params{'increment'} && !$params{'item'})
792             || ($params{'operation'} eq OPERATION_DELETE && !$params{'item'} && !$params{'increment'})
793             , 'We have the parameters required for the requested operation.');
794 0           $log->tracef("'Entering _operate_item_safely(): %s.'.", \%params);
795              
796             try {
797 0 0   0     if($params{'operation'} eq OPERATION_PUT) {
    0          
    0          
798 0           $params{'cache'}->put('key' => $params{'item_key'}, 'item' => $params{'item'});
799             }
800             elsif($params{'operation'} eq OPERATION_INCREMENT) {
801 0           $params{'cache'}->increment('key' => $params{'item_key'}, 'increment' => $params{'increment'});
802             }
803             elsif($params{'operation'} eq OPERATION_DELETE) {
804 0           $params{'cache'}->delete('key' => $params{'item_key'});
805             }
806             }
807             catch {
808 0     0     $log->debugf('_operate_item_safely(): Caught exception:%s', $_);
809 0 0 0       croak $_ unless blessed $_ && $_->can('rethrow'); ## no critic (ControlStructures::ProhibitPostfixControls)
810 0 0         if ( $_->isa('IronHTTPCallException') ) {
811 0 0 0       if( $_->status_code == HTTP_NOT_FOUND ) {
    0          
812 0           $log->warnf('_operate_item_safely(): Exception: 404 Key not found.');
813             # Does can it happen? To delete?
814 0           return 'Key not found.';
815             }
816             elsif( $_->status_code == HTTP_BAD_REQUEST
817             && $_->response_message eq 'Cannot increment or decrement non-numeric value'
818             ) {
819 0           $log->warnf('_operate_item_safely(): Exception: 400 Item not suitable for incrementation.');
820 0           return 'Item not suitable for incrementation.';
821             }
822             else {
823 0           $_->rethrow;
824             }
825             }
826             else {
827 0           $_->rethrow;
828             }
829             }
830 0     0     finally {
831 0           };
832 0           $log->tracef("'Exiting _operate_item_safely():.'.");
833 0           return;
834             }
835              
836             1;
837              
838             __END__