NAME

DBIO::Forked - Dependency-free, fork-based async layer for DBIO drivers

VERSION

version 0.900001

SYNOPSIS

See DBIO::Forked::Storage for the storage backend and DBIO::Forked::Future for the loop-free Future.

DESCRIPTION

Dependency-free, fork-based async layer for DBIO drivers. It makes any sync DBIO driver async without an async-capable database client and without an event loop: each query runs in a fork()ed child that speaks the ordinary sync driver, and the result rows are streamed back to the parent over a pipe. The parent returns a DBIO::Forked::Future immediately.

DBIO::Forked is a sibling of DBIO::Async on the same layer -- both satisfy the core DBIO::Storage::Async contract -- but they take opposite routes. DBIO::Async drives a driver's own async binding -- the non-blocking interface a DBD such as DBD::Pg (pg_async) or DBD::mysql (mysql_async) exposes itself -- through Future::IO, so it works only for drivers that offer one, but pulls in no extra client library. DBIO::Forked uses fork() + pipe + the plain sync driver in the child, so it works for every driver, including the ones that never expose an async binding (Oracle, SQLite, DB2, Sybase, ...).

ACTIVATION

Loading DBIO::Forked registers a generic forked async mode on the core base storage class (ADR 0030):

DBIO::Storage::DBI->register_async_mode( forked => 'DBIO::Forked::Storage' );

so every DBIO driver inherits it. The user then opts a connection into it per-connection, at connect time:

use DBIO::Forked;
my $schema = MyApp::Schema->connect($dsn, $user, $pass, { async => 'forked' });

A connection opened with { async => 'forked' } answers the six *_async methods (and the *_async helpers on ResultSet/Row) through DBIO::Forked::Storage; a connection opened without it stays fully synchronous (its *_async methods croak). There is no auto-selection -- the mode is explicit or absent. The core resolver constructs DBIO::Forked::Storage->new($schema) as the embedded async backend; each async query then forks a child that reconnects the sync driver fresh and runs the ordinary sync CRUD before streaming the result back.

DEPENDENCY POSTURE

Only core Perl -- fork, pipe, Storable (serialization), IO::Select (waiting) -- plus DBIO core. No Future, no Future::IO, no event loop, no async DB client. That is the whole point: turning a sync driver async pulls in none of the async ecosystem.

EXECUTION MODEL

Model A -- one short-lived fork() per query. The child inherits the entire parent memory (including the real driver's sync storage), throws away the inherited DBI handle (the fork trap that DBIO::Storage::DBI already guards against), reconnects fresh from the DBI-form connect info, runs the driver's ordinary sync CRUD (no SQL is re-implemented in DBIO::Forked), serializes the result rows back over the pipe with Storable, and exits. The parent returns a DBIO::Forked::Future bound to the pipe read fd.

TRANSACTION SEMANTICS

Model A forks per unit of work, so a multi-statement transaction cannot be spread across forks. txn_do_async therefore runs the entire transaction block in one fork()ed child: the BEGIN, every statement in the block, and the final COMMIT/ROLLBACK all execute synchronously in that single child against the freshly-reconnected sync driver. Only the block's overall result is streamed back to the parent as a DBIO::Forked::Future.

So DBIO::Forked makes a transaction async at the granularity of the whole block, not of individual statements. From the parent's side the block is non-blocking -- you get a Future immediately and the event loop (or other forks) runs while the child works -- but inside the transaction there are no loop ticks between statements: the statements do not each return asynchronously, and you cannot issue one statement, yield to the loop, and issue the next on the same pinned transaction.

If you need genuine per-statement async transactions over a pinned connection (send a statement non-blocking, let the loop do other work, then send the next), use a connection-based backend instead -- DBIO::Async (Future::IO over the driver's own async binding) or a native EV add-on (DBIO::PostgreSQL::EV, DBIO::MySQL::EV) -- not DBIO::Forked. This is an intrinsic property of Model A, not a defect.

AUTHOR

DBIO Authors

COPYRIGHT AND LICENSE

Copyright (C) 2026 DBIO Authors

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