DBIx-QuickORM
view release on metacpan or search on metacpan
lib/DBIx/QuickORM/Manual/Async.pm view on Meta::CPAN
throws an exception. Use C<forked> instead on those backends.
=head2 aside - asynchronous on a separate connection
my $iter = $h->aside->iterator;
An C<aside> query is the same driver-level async mechanism, but run on a
B<separate> connection obtained from the ORM rather than on your primary
connection. Because the primary connection stays free, an aside query does
B<not> block other work on it: you can run normal synchronous queries, open
transactions, and even start more aside or forked queries while it is in
flight.
Reach for C<aside> when you want async behavior but do not want to tie up your
main connection. It still requires a driver that supports async.
=head2 forked - run the query in a child process
my $iter = $h->forked->iterator;
A C<forked> query C<fork()>s a child process with its own database connection,
runs the query there, and streams the results back to the parent. The parent
never blocks while the child works.
This is the way to get asynchronous behavior on databases whose driver has no
native async support, such as SQLite. Like C<aside>, it runs off your primary
connection, so it does not block it.
The results cross from child to parent over a pipe (via L<Atomic::Pipe>) with
C<zstd> compression, so more rows fit in the pipe buffer and less data hits the
wire. You do not interact with the transport directly - you just pull rows.
=head1 RUNNING AN ASYNC QUERY
The pattern is the same for all three modes. Pick the mode, get an
L<DBIx::QuickORM::Iterator>, poll C<ready()>, then drain the results:
my $iter = $h->async->iterator; # or ->aside->iterator / ->forked->iterator
until ($iter->ready) {
do_something_useful(); # the DB is working in the background
}
while (my $row = $iter->next) {
... # rows arrive here
}
C<ready()> is non-blocking: it polls and returns true once results are
available (for a synchronous handle it is always true). C<next()> pulls one row
at a time; if results have not arrived yet it blocks until they do.
C<all()>, C<count()>, C<iterate()>, and C<first()>/C<one()> in C<data_only>
mode are B<not> available on an async handle - C<all()> in particular would
have to block, defeating the purpose. Use C<iterator()> and its C<ready()>
method instead.
=head2 Single-row async results
C<one()> and C<first()> on an async handle return a
L<DBIx::QuickORM::Row::Async> placeholder rather than a real row. It is a
transparent proxy: it is true while pending, and the first time you call a real
method on it (or check C<isa>/C<can>/C<DOES>) it materializes the row from the
arrived results and swaps itself out in place, so you transparently end up
holding the real row object. If the query returned no data or was cancelled the
proxy becomes invalid - it is false in boolean context and method calls croak.
my $row = $h->async->one; # placeholder, true while pending
print $row->field('name'); # blocks if needed, then forwards to the real row
Inserts behave the same way: an C<insert> on an async handle returns a
placeholder row that materializes once the insert result arrives.
=head1 CANCELLATION
The iterator has no C<cancel> method. There are two real ways to stop an
in-flight query:
=over 4
=item Drop the iterator to finalize the underlying statement.
When the L<DBIx::QuickORM::Iterator> from an C<async>/C<aside>/C<forked> query
goes out of scope, the async statement handle it holds finalizes itself:
cancelling the query if it can and no result has arrived yet, otherwise waiting
for it to finish, and then releasing the slot it held on the connection. So
letting the iterator fall out of scope is how you abandon a multi-row query.
=item Call C<< $row->cancel >> on a single-row async result.
C<one>, C<first>, and C<insert> on an async handle hand back a
L<DBIx::QuickORM::Row::Async> placeholder. Calling C<< $row->cancel >> on it
cancels the in-flight query (when no result has arrived yet) and marks the
placeholder invalid. A placeholder dropped before it materializes is finalized
the same way automatically.
=back
Whether an in-flight query can truly be cancelled depends on the mode and the
dialect. Driver-level C<async> and C<aside> queries can be cancelled only when
the dialect supports it; C<forked> queries can always be cancelled (the child
process is signalled and reaped). When a query cannot be cancelled, finalizing
it waits for it to finish instead. Either way you normally do not have to clean
up by hand - scope-drop finalization handles it.
=head1 RULES AND CONSTRAINTS
These rules keep the connection state consistent. Most apply to driver-level
C<async> specifically, because it shares your primary connection.
=over 4
=item One driver-async query at a time per connection.
Because an C<async> query occupies the primary connection, you must finish (or
cancel) it before running anything else on that connection. Starting a second
query while an async query is still in flight throws an exception telling you
the running query must be completed first. Drain the iterator (or drop it to
finalize the query), or C<< $row->cancel >> a single-row placeholder, before
issuing the next query.
=item No transactions while an async query is active.
You cannot open a transaction while a driver-async query is in flight on the
( run in 2.485 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )