The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

CHI::Cascade - a cache dependencies (cache and like 'make' utility concept)

SYNOPSIS

    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 ($target_name, $values_of_depends) = @_;

            # $values_of_depends == {
            #     unique_name_other1 => $value_1,
            #     unique_name_other2 => $value_2
            # }

            # Now we can calcualte $value
            return $value;
        }
    );

    $cascade->rule(
        target  => 'unique_name_other1',
        depends => 'unique_name_other3',
        code    => sub {
            my ($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');

DESCRIPTION

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.

CONSTRUCTOR

$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. Now there is only one option: chi - instance of CHI object.

METHODS

rule( %options )

To add new rule to CHI::Cascade object. All rules should be added before first "run" method

The keys of %options are:

target

A target for "run" and for searching of "depends". It can be as scalar text or Regexp object created through qr//

depends

The list or a scalar value of dependencies - the list of targets which the current rule is dependent. Each item can be scalar value (exactly matched target) or code reference which will be executed during matching of target. A code subroutine will get a parameters from =~ operator against target matching by qr// operator(not tested while) - please see the section "EXAMPLE" for this example.

code

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. This subroutine will get parameters: $_[0] - flat text of current target and hashref of values of dependencies. 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).

run( $target )

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.

touch( $target )

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.

STATUS

This module is experimental and not finished for new features ;-) Please send me issues through https://github.com/Perlover/CHI-Cascade page

CHI::Cascade & make

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

FEATURES

The features of this module are following:

Computing inside process

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.

Non-blocking computing for concurrent processes

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.

Each target is splitted is two items in cache

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.

EXAMPLE

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_' . $_[0] },
        code    => sub {
            my ($target_name, $values_of_depends) = @_;

            # $this_name == 'unique_name_3' if $cascade->run('unique_name_3') was
            # $values_of_depends == {
            #     unique_name_other3 => $value_ref_3
            # }
        }
    );

    $cascade->rule(
        target  => qr/unique_name_other_(.*)/,
        code    => sub {
            my ($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

AUTHOR

This module has been written by Perlover <perlover@perlover.com>

LICENSE

This module is free software and is published under the same terms as Perl itself.

SEE ALSO

CHI - mandatory
CHI::Driver::File - file caching