DBIO-Async

 view release on metacpan or  search on metacpan

lib/DBIO/Async/Storage.pm  view on Meta::CPAN

=head1 VERSION

version 0.900001

=head1 DESCRIPTION

The C<future_io> async backend (ADR 0030): one loop-agnostic transport that
drives a driver's own non-blocking DBD binding through L<Future::IO>. The
event loop is chosen by installing a C<Future::IO::Impl::*> adapter, not by
picking a distribution.

The B<Model-B orchestration> -- connect-info normalisation, the CRUD runner
(L<DBIO::Storage::Async/_run_crud> with its pooled and pinned runners), INSERT
returned-columns mapping, L<DBIO::Storage::Async/txn_do_async> bracketing
through the generic L<DBIO::Storage::Async::TransactionContext>, and the
L<DBIO::Storage::Async/pipeline> scaffold -- is inherited concretely from
L<DBIO::Storage::Async> (ADR 0030 §4). This class supplies only the
B<transport>: the L<Future::IO> query execution (L</_query_async> /
L</_query_async_pinned> over L</_await_query_result>), the L<Future>
implementation (L</future_class>) and the connection pool (L</pool>), plus the
DB-specific seam hooks a concrete DBD subclass fills in.

=head1 METHODS

=head2 future_class

Returns C<'Future'> -- this backend uses L<Future> from CPAN.

=head2 transport_capabilities

  my @caps = DBIO::Async::Storage->transport_capabilities;   # ('on_connect_replay')

Class method (see L<DBIO::Storage::Async/transport_capabilities>). Declares the
named transport capabilities this C<future_io> transport provides, so
L<DBIO::Storage::DBI/_async_storage> lets an async extension layer that declares
a matching C<required_transport_capabilities> compose onto it (and croaks on a
shortfall rather than silently dropping the feature).

This transport declares C<on_connect_replay>: its pool
(L<DBIO::Async::Pool>, a L<DBIO::Storage::PoolBase>) drives core's
L<DBIO::Storage::Async/_setup_pool_connection> on every freshly-spawned
connection, and the C<< { dbh => $dbh } >> connection shape is handled by the
base C<_run_pool_connect_statement> -- so each pooled async connection replays
the owning sync storage's C<on_connect_do>/C<on_connect_call> (and the
C<on_disconnect_*> actions at shutdown) end to end (karr #68 seam). A concrete
DBD subclass inherits this obligation for free.

=head2 pool

Returns the L<DBIO::Async::Pool> connection pool, created lazily on first
access. Fed the per-spawn C<conninfo_provider> when an AccessBroker is attached
(see L<DBIO::Storage::Async/_conninfo_provider>), otherwise the static
conninfo.

=head2 _async_broker_conninfo

  my $conninfo = $storage->_async_broker_conninfo($mode);

AccessBroker seam (see L<DBIO::Storage::Async/ACCESSBROKER CONSUMPTION>):
return one fresh, storage-native conninfo for a single new pool connection,
built from the current broker credentials via the inherited normalisation.

=head2 _query_async

Transport override: execute a query on a freshly-acquired pooled connection,
releasing it once the Future is ready.

Receives SQL in the C<sql_maker> C<?>-placeholder dialect (the core #70 seam
contract) and shapes it into the driver's own dialect B<internally>, up front,
through L</_transform_sql> before the query reaches the wire -- callers no longer
pre-shape. The C<?>-E<gt>C<$N>-style rewrite is idempotent on already-shaped SQL
(no bare C<?> left to touch), which is what keeps the transition window safe
while core still shapes at its own call site.

=head2 _query_async_pinned

Transport override: like L</_query_async> but runs on the supplied pinned
connection and does B<not> release it -- used for queries inside a pinned
transaction. Shapes the incoming C<?>-dialect SQL internally through
L</_transform_sql> exactly as L</_query_async> does.

=head2 _await_readable

  my $future = $storage->_await_readable($conn);

Returns a Future that becomes ready once the connection's socket is readable.
The single seam through which this transport touches L<Future::IO>: a concrete
driver's L</_collect_result> that must wait for more of a result also calls this
rather than re-implementing the fd wrapping. The filehandle C<poll> needs is
supplied by L</_conn_poll_fh>.

=head2 _conn_poll_fh

  my $fh = $storage->_conn_poll_fh($conn);

The stable poll filehandle for C<$conn>, created lazily on first use and cached
on the connection for its whole life. L<Future::IO-E<gt>poll> requires a real
filehandle, but a driver's L</_conn_fileno> hands back the raw integer socket fd
(e.g. DBD::Pg's C<pg_socket>). The C<< '+<&' >> open mode dups that fd (via
C<dup(2)>) into an independent filehandle, so closing this handle never touches
the driver's own socket.

The dup is cached B<per connection> for a correctness reason, not merely for
speed. L<Future::IO> watches the filehandle and, once the poll resolves,
unwatches it B<by its fileno> -- but its impls mark the Future done I<before>
they unwatch. A fresh-dup-per-poll handle closed the moment its poll resolved
would already be gone when the loop unwatches: C<< $fh->fileno >> returns
C<undef>, the loop's watch table is corrupted, and the freed fd -- reused by the
next dup -- collides with the stale watch (observed as
C<pg_ready: No asynchronous query is running> on the following query). Reusing
one stable handle for every poll on a connection keeps the fileno valid across
the whole watch/unwatch cycle. The dup fd dies with the connection it mirrors,
when C<$conn> is torn down.

Cached on the connection hashref under the reserved key C<_dbio_async_poll_fh>;
a driver whose connection is not a hashref must supply its own caching.

=head2 _await_conn_ready

  my $future = $storage->_await_conn_ready($conn);



( run in 1.774 second using v1.01-cache-2.11-cpan-007c89162af )