AnyEvent-Task
view release on metacpan or search on metacpan
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
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'],
$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.
],
'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 {
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.
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 )