The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Time::Progress::Stored - Report progress + store and retrieve the current status

DESCRIPTION

This module helps if you have a long running process which reports progress, but you need to actually display the progress to the user in a different process.

Typically this is a long running web request in the web server or in a job queue worker, while the web browser periodically sends Ajax requests to get updated on the current progress status to show using a progress bar.

Time::Progress::Stored stores the progress report as the worker performs its job, and retrieves it from elsewhere where the report is needed.

The report is a hashref with the following details:

    | id                | "progress-1454518121172"   |
    | max               | 262                        |
    | current           | 2                          |
    | activity          | "Importing Users"          |
    | elapsed_seconds   | "1"                        |
    | elapsed_time      | " 0:01"                    |
    | finish_time       | "Wed Feb 3 16:50:54 2016"  |
    | percent           | 0.8                        |
    | percent_string    | " 0.8%"                    |
    | remaining_seconds | "130"                      |
    | remaining_time    | " 2:10"                    |
    | is_done           | 0                          |

SYNOPSIS

    ### In the worker process

    my $progess_id = ...; # Probably comes from the client
    my $max = @items * 2; # Let's say two passes over @items

    my $redis = Redis->new( ... );
    my $progress = Time::Progress::Stored->new({
        max         => $max,
        progress_id => $progress_id,
        storage     => Progress::Stored::Storage::Redis->new({ redis => $redis }),
    });

    # Do the work
    for my $item (@items) {
        # Do stuff
        $progress->advance("Doing the first thing");
    }
    for my $item (@items) {
        # Do other stuff
        $progress->advance("Doing other stuff with $item->{name}");
    }

    $progress->done();


    ### HTTP endpoint, e.g. a Mojo controller for /progress/:progress_id

    sub get {
        my $self = shift;
        my $progress_id = $self->param("progress_id");

        my $storage = Time::Progress::Stored::Storage::Redis->new({
            redis => $self->redis,
        });
        my $response_json = $storage->retrieve($progress_id)
            or return $self->render(status => 404, json => {});

        return $self->render(json => { progress => $response_json });
    }

Synopsis breakdown

Worker

As the long running piece of work comes in, start the progress reporting.

Create the $progress object with a storage to store the progress reports. Initialize it with the number of actions you want to perform.

For each time something is done, call $progress->advance(). This might update the actual report.

By default only about 100 steps will be reported (each percent progressed). You can increase this by specifying the attribute "report_every", e.g. to report every single time something happens, set it to 1 (this might slow things down though).

"advance" can optionally be called with an activity description string, indicating what's happening right now. This can be large scale steps, or include details about this specific action being performed.

At the end, call $progress->done, just to make sure the reporting knows it has reached the end. This will set is_done: 1 in the report.

HTTP endpoint

The client should send requests to the endpoint periodically to get the current progress report. The request contains the progress id, and it's simply fetched and returned as a JSON payload.

See above for the contents of the payload.

Client making a request to a long running web request

This is for when the long running process is running as one web request.

A web page would typically provide a progress id while making the initial Ajax request to kick off the long running process.

The client then sends Ajax requests to the HTTP endpoint (using the id) every second to get updates on the progress. It does this until the long running progress has responded.

If you look at the report payload you'll see there is a variety of information to provide user feedback, e.g. percent done, time left, what's going on etc.

Client making a request to a asynchronous worker process

This is for when the long running process is running as a background process, e.g. in a job queue.

A web page would typically kick off the long running process. Instead of the client passing in a progress id, the $progress object defaults to a unique id (or you can just make one up yourself), and the HTTP endpoint can report $progress->progress_id back to the client in the response.

The client then sends Ajax requests to the HTTP endpoint (using the id) every second to get updates on the progress. It does this until the progress report "is_done" is set to 1.

Storage backends

Time::Progress::Stored can use various backends to store the status.

Current backends are Redis and Memory. Memory is mainly for testing and not very useful for real life scenarios.

The backends are tiny and it would be simple to write another backend for e.g. a file, or using one of the Cache or CHI modules. Patches more than welcome.

Pitfalls

If you're testing with a single threaded test server like Morbo, the web browser's Ajax requests won't be processed until the long running process has finished, so you'll never see the progress until it's all done.

ATTRIBUTES

max

The number of times you're planning to call ->advance().

storage

A Storage object to store reports in.

Default: Time::Progress::Stored::Storage::Memory, but that's probably only useful for testing.

progress_id

The id to identify this progress report with. Either set by the client, or defaulted to a unique URL-param safe value you can return to the client.

report_every

Number of calls to ->advance() for every time a new progress report is generated and stored.

Default: a value that results in a report for every percent of progress.

current

The current iteration. You could in theory set this yourself, but it's recommended to call ->advance().

current_activity

Same as ->current, but for the current activity.

METHODS

advance($activity?)

Advance the progress one iteration. Optionally set the current $activity.

done()

Mark the progress as being completed, and set the is_done key in the report.

DEVELOPMENT

Author

Johan Lindstrom, <johanl [AT] cpan.org>

Contributors

Source code

https://github.com/jplindstrom/p5-Time-Progress-Stored

Bug reports

Please report any bugs or feature requests on GitHub:

https://github.com/jplindstrom/p5-Time-Progress-Stored/issues.

Caveats

COPYRIGHT & LICENSE

Copyright 2016- Broadbean Technologies, All Rights Reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

ACKNOWLEDGEMENTS

Thanks to Broadbean for providing time to open source this during one of the regular Hack-days.

This module uses the excellent Time::Progress module, which you should be using if you just need a progress bar in a command line script.