AnyEvent-Task

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

0.801   2014-02-15
  * Bugfix: Fix memory leak of client objects.
  * Change: Make hung worker timeout actually terminate the
    worker to free up resources immediately.

0.800   2014-02-15
  * Backwards-incompatible change: When multiple requests are
    queued up on a checkout, if one of the requests throws an
    error all the pending requests are removed from the queue.
    This makes a non-nested sequence of method-calls on a
    checkout less dangerous and more like the synchronous code
    it is mimicing.
  * Removed vestigal parts of an undocumented feature that was
    broken several releases ago: In non-void context, methods
    on a checkout used to return a guard that when destroyed
    would cancel the remote method call. Instead, now you should
    use the throw_fatal_error method on the checkout. The checkout
    will then throw errors every time it is accessed and should
    be discarded.
  * Documented max_checkouts feature for coping with memory leaks
  * Major documentation updates

README  view on Meta::CPAN

NAME
    AnyEvent::Task - Client/server-based asynchronous worker pool

SYNOPSIS 1: PASSWORD HASHING
  Server
        use AnyEvent::Task::Server;
        use Authen::Passphrase::BlowfishCrypt;

        my $dev_urandom;
        my $server = AnyEvent::Task::Server->new(
                       name => 'passwd-hasher',
                       listen => ['unix/', '/tmp/anyevent-task.socket'],

README  view on Meta::CPAN

          $cv->send;
        });

        $cv->recv;

  Output
        username: jimmy, email: jimmy@example.com

DESCRIPTION
    The synopses make this module look much more complicated than it
    actually is. In a nutshell, a synchronous worker process is forked off
    by a server whenever a client asks for one. The client keeps as many of
    these workers around as it wants and delegates tasks to them
    asynchronously.

    Another way of saying that is that AnyEvent::Task is a
    pre-fork-on-demand server (AnyEvent::Task::Server) combined with a
    persistent worker-pooled client (AnyEvent::Task::Client).

    The examples in the synopses are complete stand-alone programs. Run the
    server in one window and the client in another. The server will remain
    running but the client will exit after printing its output. Typically
    the "client" programs would be embedded in a server program such as a
    web-server.

README  view on Meta::CPAN

                        ],
              'timers' => {
                            'computing some operation' => [
                                                            '0.024089061050415',
                                                            '1.02470206105041'
                                                          ]
                          }
            };

ERROR HANDLING
    In a synchronous program, if you expected some operation to throw an
    exception you might wrap it in "eval" like this:

        my $crypted;

        eval {
          $crypted = hash('secret');
        };

        if ($@) {
          say "hash failed: $@";
        } else {
          say "hashed password is $crypted";
        }

    But in an asynchronous program, typically "hash" would initiate some
    kind of asynchronous operation and then return immediately, allowing the
    program to go about other tasks while waiting for the result. Since the
    error might come back at any time in the future, the program needs a way
    to map the exception that is thrown back to the original context.

    AnyEvent::Task accomplishes this mapping with Callback::Frame.

    Callback::Frame lets you preserve error handlers (and "local" variables)
    across asynchronous callbacks. Callback::Frame is not tied to
    AnyEvent::Task, AnyEvent or any other async framework and can be used
    with almost all callback-based libraries.

    However, when using AnyEvent::Task, libraries that you use in the client
    must be AnyEvent compatible. This restriction obviously does not apply
    to your server code, that being the main purpose of this module:
    accessing blocking resources from an asynchronous program. In your
    server code, when there is an error condition you should simply "die" or
    "croak" as in a synchronous program.

    As an example usage of Callback::Frame, here is how we would handle
    errors thrown from a worker process running the "hash" method in an
    asychronous client program:

        use Callback::Frame;

        frame(code => sub {

          $client->checkout->hash('secret', sub {

README  view on Meta::CPAN

    unlikely to raise an exception so maybe that's a bad example. On the
    other hand, maybe it's a really good example: In addition to errors that
    occur while running your callbacks, AnyEvent::Task uses Callback::Frame
    to throw errors if the worker process times out, so if the bcrypt "cost"
    is really cranked up it might hit the default 30 second time limit.

  Rationale for Callback::Frame
    Why not just call the callback but set $@ and indicate an error has
    occurred? This is the approach taken with AnyEvent::DBI for example. I
    believe the Callback::Frame interface is superior to this method. In a
    synchronous program, exceptions are out-of-band messages and code
    doesn't need to locally handle them. It can let them "bubble up" the
    stack, perhaps to a top-level error handler. Invoking the callback when
    an error occurs forces exceptions to be handled in-band.

    How about having AnyEvent::Task expose an error callback? This is the
    approach taken by AnyEvent::Handle for example. I believe
    Callback::Frame is superior to this method also. Although separate
    callbacks are (sort of) out-of-band, you still have to write error
    handler callbacks and do something relevant locally instead of allowing
    the exception to bubble up to an error handler.

README  view on Meta::CPAN

    server is even started. The client will continue trying to connect to
    the server to obtain worker processes until either the server starts or
    the checkout's timeout period lapses. As well as freeing you from having
    to start your services in the "right" order, this also means servers can
    be restarted without throwing any errors (aka "zero-downtime restarts").

    The client even decides how many minimum workers should be in the pool
    upon start-up and how many maximum workers to acquire before checkout
    creation requests are queued. The server is really just a dumb
    fork-on-demand server and most of the sophistication is in the
    asynchronous client.

SEE ALSO
    The AnyEvent::Task github repo
    <https://github.com/hoytech/AnyEvent-Task>

    In order to handle exceptions in a meaningful way with this module, you
    must use Callback::Frame. In order to maintain seamless request logging
    across clients and workers, you should use Log::Defer.

    There are many modules on CPAN similar to AnyEvent::Task.

lib/AnyEvent/Task.pm  view on Meta::CPAN


1;


__END__

=encoding utf-8

=head1 NAME

AnyEvent::Task - Client/server-based asynchronous worker pool

=head1 SYNOPSIS 1: PASSWORD HASHING

=head2 Server

    use AnyEvent::Task::Server;
    use Authen::Passphrase::BlowfishCrypt;

    my $dev_urandom;
    my $server = AnyEvent::Task::Server->new(

lib/AnyEvent/Task.pm  view on Meta::CPAN

    $cv->recv;

=head2 Output

    username: jimmy, email: jimmy@example.com



=head1 DESCRIPTION

The synopses make this module look much more complicated than it actually is. In a nutshell, a synchronous worker process is forked off by a server whenever a client asks for one. The client keeps as many of these workers around as it wants and deleg...

Another way of saying that is that L<AnyEvent::Task> is a pre-fork-on-demand server (L<AnyEvent::Task::Server>) combined with a persistent worker-pooled client (L<AnyEvent::Task::Client>).

The examples in the synopses are complete stand-alone programs. Run the server in one window and the client in another. The server will remain running but the client will exit after printing its output. Typically the "client" programs would be embedd...

Note that the client examples don't implement error checking (see the L<ERROR HANDLING> section).

A server is started with C<< AnyEvent::Task::Server->new >>. This constructor should be passed in at least the C<listen> and C<interface> arguments. Keep the returned server object around for as long as you want the server to be running. C<listen> is...

A client is started with C<< AnyEvent::Task::Client->new >>. You only need to pass C<connect> to this constructor which is an array ref containing the host and service options to be passed to L<AnyEvent::Socket>'s C<tcp_connect>. Keep the returned cl...

lib/AnyEvent/Task.pm  view on Meta::CPAN

                                                        '1.02470206105041'
                                                      ]
                      }
        };




=head1 ERROR HANDLING

In a synchronous program, if you expected some operation to throw an exception you might wrap it in C<eval> like this:

    my $crypted;

    eval {
      $crypted = hash('secret');
    };

    if ($@) {
      say "hash failed: $@";
    } else {
      say "hashed password is $crypted";
    }

But in an asynchronous program, typically C<hash> would initiate some kind of asynchronous operation and then return immediately, allowing the program to go about other tasks while waiting for the result. Since the error might come back at any time i...

AnyEvent::Task accomplishes this mapping with L<Callback::Frame>.

Callback::Frame lets you preserve error handlers (and C<local> variables) across asynchronous callbacks. Callback::Frame is not tied to AnyEvent::Task, AnyEvent or any other async framework and can be used with almost all callback-based libraries.

However, when using AnyEvent::Task, libraries that you use in the client must be L<AnyEvent> compatible. This restriction obviously does not apply to your server code, that being the main purpose of this module: accessing blocking resources from an a...

As an example usage of Callback::Frame, here is how we would handle errors thrown from a worker process running the C<hash> method in an asychronous client program:

    use Callback::Frame;

    frame(code => sub {

      $client->checkout->hash('secret', sub {
        my ($checkout, $crypted) = @_;
        say "Hashed password is $crypted";

lib/AnyEvent/Task.pm  view on Meta::CPAN

      say "Full back-trace: $back_trace";

    })->(); ## <-- frame is created and then immediately executed

Of course if C<hash> is something like a bcrypt hash function it is unlikely to raise an exception so maybe that's a bad example. On the other hand, maybe it's a really good example: In addition to errors that occur while running your callbacks, L<An...



=head2 Rationale for Callback::Frame

Why not just call the callback but set C<$@> and indicate an error has occurred? This is the approach taken with L<AnyEvent::DBI> for example. I believe the L<Callback::Frame> interface is superior to this method. In a synchronous program, exceptions...

How about having AnyEvent::Task expose an error callback? This is the approach taken by L<AnyEvent::Handle> for example. I believe Callback::Frame is superior to this method also. Although separate callbacks are (sort of) out-of-band, you still have ...

In servers, Callback::Frame helps you maintain the "dynamic state" (error handlers and dynamic variables) installed for a single connection. In other words, any errors that occur while servicing that connection will be able to be caught by an error h...

Callback::Frame provides an error handler stack so you can have a top-level handler as well as nested handlers (similar to nested C<eval>s). This is useful when you wish to have a top-level "bail-out" error handler and also nested error handlers that...

Callback::Frame is designed to be easily used with callback-based libraries that don't know about Callback::Frame. C<fub> is a shortcut for C<frame> with just the C<code> argument. Instead of passing C<sub { ... }> into libraries you can pass in C<fu...

It's important that all callbacks be created with C<fub> (or C<frame>) even if you don't expect them to fail so that the dynamic context is preserved for nested callbacks that may. An exception is the callbacks provided to AnyEvent::Task checkouts: T...

lib/AnyEvent/Task.pm  view on Meta::CPAN

But since a single process can handle many requests in a row without exiting, they are more like persistent FastCGI processes. The difference however is that while a client holds a checkout it is guaranteed an exclusive lock on that process (useful f...

The fundamental difference between the AnyEvent::Task protocol and HTTP is that in AnyEvent::Task the client is the dominant protocol orchestrator whereas in HTTP it is the server.

In AnyEvent::Task, the client manages the worker pool and the client decides if/when worker processes should terminate. In the normal case, a client will just return the worker to its worker pool. A worker is supposed to accept commands for as long a...

The client decides the timeout for each checkout and different clients can have different timeouts while connecting to the same server.

Client processes can be started and checkouts can be obtained before the server is even started. The client will continue trying to connect to the server to obtain worker processes until either the server starts or the checkout's timeout period lapse...

The client even decides how many minimum workers should be in the pool upon start-up and how many maximum workers to acquire before checkout creation requests are queued. The server is really just a dumb fork-on-demand server and most of the sophisti...




=head1 SEE ALSO

L<The AnyEvent::Task github repo|https://github.com/hoytech/AnyEvent-Task>

In order to handle exceptions in a meaningful way with this module, you must use L<Callback::Frame>. In order to maintain seamless request logging across clients and workers, you should use L<Log::Defer>.



( run in 0.266 second using v1.01-cache-2.11-cpan-0d8aa00de5b )