CHI::Cascade - a cache dependencies (cache and like 'make' utility concept)
use CHI; use CHI::Cascade; $cascade = CHI::Cascade->new(chi => CHI->new(...)); $cascade->rule( target => 'unique_name', depends => ['unique_name_other1', 'unique_name_other2'], code => sub { my ($rule, $target_name, $values_of_depends) = @_; # $values_of_depends == { # unique_name_other1 => $value_1, # unique_name_other2 => $value_2 # } # $rule->target eq $target_name # $rule->depends === ['unique_name_other1', 'unique_name_other2'] # $rule->dep_values == $values_of_depends # $rule->params == { a => 1, b => 2 } # Now we can calcualte $value return $value; }, params => { a => 1, b => 2 } ); $cascade->rule( target => 'unique_name_other1', depends => 'unique_name_other3', code => sub { my ($rule, $target_name, $values_of_depends) = @_; # $values_of_depends == { # unique_name_other3 => $value_3 # } # computing here return $value; } ); $value_of_this_target = $cascade->run('unique_name');
This module is the attempt to use a benefits of caching and 'make' concept. If we have many an expensive tasks and want to cache it we can split its to small expsnsive tasks and to describe dependencies for cache items.
This module is experimental yet. I plan to improve it near time but some things already work. You can take a look for t/* tests as examples.
$cascade = CHI::Cascade->new( %options )
This method constructs a new CHI::Cascade object and returns it. Key/value pair arguments may be provided to set up the initial state. Options are:
CHI::Cascade
Required. Instance of CHI object. The CHI::Cascade doesn't construct this object for you. Please create instance of CHI yourself.
CHI
Optional. Default is never. This is not busy_lock option of CHI! This is amount of time (to see "DURATION EXPRESSIONS" in CHI) until all target locks expire. When a target is recomputed it is locked. If process is to be recomputing target and it will die or OS will be hangs up we can dead locks and locked target will never recomputed again. This option helps to avoid it. You can set up a special busy_lock for rules too.
busy_lock
To add new rule to CHI::Cascade object. All rules should be added before first "run" method
The keys of %options are:
Required. A target for "run" and for searching of "depends". It can be as scalar text or Regexp object created through qr//
Regexp
qr//
Optional. The scalar, arrayref or coderef values of dependencies. This is the definition of target(s) from which this current rule is dependent. If depends is:
It should be plain text of single dependence of this target.
An each item of list can be scalar value (exactly matched target) or code reference. If item is coderef it will be executed as $coderef->( $rule, $rule->qr_params ) and should return a scalar value as current dependence for this target at runtime (the API for coderef parameters was changed since v0.16)
This subroutine will be executed every time inside run method if necessary and with parameters as: $coderef->( $rule, $rule->qr_params ) (API was changed since v0.16). It should return scalar or arrayref. The returned value is scalar it will be considered as single dependence of this target and the behavior will be exactly as described for scalar in this paragraph. If the returned value is arrayref it will be considered as list of dependencies for this target and the behavior will be exactly as described for arrayref in this paragraph.
Required. The code reference for computing a value of this target. Will be executed if no value in cache for this target or any dependence or dependences of dependences and so on will be recomputed. Will be executed as $code->( $rule, $target, $hashref_to_value_of_dependencies ) (The API of running this code was changed since v0.10)
An instance of CHI::Cascade::Rule object. You can use it object as accessor for some current executed target data (plain text of target, for getting of parameters and so on). Please to see CHI::Cascade::Rule
A current target as plain text (what a target the $cascade got from run method)
A hash reference of values of all dependencies for current target. Keys in this hash are flat strings of dependecies and values are computed or cached ones.
This module should guarantee that values of dependencies will be valid values even if value is undef. This code can return undef value as a valid code return but author doesn't recommend it. If CHI::Cascade could not get a valid values of all dependencies of current target before execution of this code the last will not be executed (The run will return undef).
undef
run
You can pass in your code any additional parameters by this option. These parameters are accessed in your code through params method of CHI::Cascade::Rule instance object.
Optional. Default is "busy_lock" of constructor or never if first is not defined. This is not busy_lock option of CHI! This is amount of time (to see "DURATION EXPRESSIONS" in CHI) until target lock expires. When a target is recomputed it is locked. If process is to be recomputing target and it will die or OS will be hangs up we can dead locks and locked target will never recomputed again. This option helps to avoid it.
This method makes a cascade computing if need and returns value for this target If any dependence of this target of any dependencies of dependencies were recomputed this target will be recomputed too.
This method refreshes the time of this target. Here is analogy with touch utility of Unix and behaviour as make after it. After "touch" all targets are dependent from this target will be recomputed at next "run" with an appropriate ones.
It's like a removing of target file in make. You can force to recompute target by this method. It will remove target marker if one exists and once when cascade will need target value it will be recomputed. In a during recomputing of course cascade will return an old value if one exists in cache.
This module is experimental and not finished for new features ;-) Please send me issues through https://github.com/Perlover/CHI-Cascade page
Here simple example how it works. Here is a direct analogy to Unix make utility:
In CHI::Cascade: In make: rule rule depends prerequisites code commands run( rule_name ) make target_name
The features of this module are following:
If module needs to compute item for cache we compute inside process (no forks) For web applications it means that one process for one request could take a some time for computing. But other processes will not wait and will get either old previous computed value or undef value.
If other process want to get data from cache we should not block it. So concurrent process can get an old data if new computing is run or can get undef value. A concurrent process should decide itself what it should do after it - try again after few time or print some message like 'Please wait and try again' to user.
For optimization this module keeps target's info by separately from value item. A target item has lock & timestamp fields. A value item has a computed value.
For example please to see the SYNOPSIS
When we prepared a rules and a depends we can:
If unique_name_other1 and/or unique_name_other2 are(is) more newer than unique_name the unique_name will be recomputed. If in this example unique_name_other1 and unique_name_other2 are older than unique_name but the unique_name_other3 is newer than unique_name_other1 then unique_name_other1 will be recomputed and after the unique_name will be recomputed.
And even we can have a same rule:
$cascade->rule( target => qr/^unique_name_(.*)$/, depends => sub { 'unique_name_other_' . $_[1] }, code => sub { my ($rule, $target_name, $values_of_depends) = @_; # $rule->qr_params === ( 3 ) # $target_name == 'unique_name_3' if $cascade->run('unique_name_3') was # $values_of_depends == { # unique_name_other_3 => $value_ref_3 # } } ); $cascade->rule( target => qr/unique_name_other_(.*)/, code => sub { my ($rule, $target_name, $values_of_depends) = @_; ... } );
When we will do:
$cascade->run('unique_name_52');
$cascade will find rule with qr/^unique_name_(.*)$/, will make =~ and will find a depend as unique_name_other_52
This module has been written by Perlover <perlover@perlover.com>
This module is free software and is published under the same terms as Perl itself.
An instance of this object can be used in your target codes.
This object is used for cache.
Recommended if you have the Memcached
Recommended if you want to use the file caching instead the Memcached for example
To install CHI::Cascade, copy and paste the appropriate command in to your terminal.
cpanm
cpanm CHI::Cascade
CPAN shell
perl -MCPAN -e shell install CHI::Cascade
For more information on module installation, please visit the detailed CPAN module installation guide.