NAME
DBIO::Forked::Storage - Fork-based async storage skeleton — make any sync DBIO driver async
VERSION
version 0.900001
DESCRIPTION
Fork-based async storage backend subclassing core DBIO::Storage::Async. It makes any sync DBIO driver async without an async-capable client and without an event loop, via fork()-per-query (Model A, see DBIO::Forked): the child reconnects the sync driver fresh from the stored DBI-form connect info, runs the driver's ordinary sync CRUD (no SQL is re-implemented here), serializes the result rows back over a pipe with Storable, and exits; the parent returns a DBIO::Forked::Future bound to the pipe read fd.
METHODS
new
my $storage = DBIO::Forked::Storage->new($schema);
Construct the fork-based async backend for $schema. The schema reference is weakened (the schema owns the storage, not the other way round). Connect info is supplied separately via "connect_info".
future_class
Returns 'DBIO::Forked::Future' -- the loop-free, pipe-backed Future this backend hands out.
connect_info
$storage->connect_info([ $dsn, $user, $pass, \%attrs, \%dbio_opts ]);
Store the DBI-form connect info verbatim (the core resolver passes the sync storage's _connect_info straight through). Returns the stored value.
Informational in Model A: the forked child runs the inherited sync storage's own CRUD, and that storage reconnects itself in the child via its inherited fork handling (see docs/adr/0002). This stored connect info is therefore not consumed on the query path -- it is kept as latent diagnostics / raw material for a possible future "fresh storage" variant.
select_async
my $future = $storage->select_async($source, $select, $where, $attrs);
Run a SELECT in a forked child and return a DBIO::Forked::Future of the result rows.
select_single_async
Like "select_async" but resolves to the first row only.
insert_async
my $future = $storage->insert_async($source, \%vals);
update_async
my $future = $storage->update_async($source, \%vals, \%where);
delete_async
my $future = $storage->delete_async($source, \%where);
txn_do_async
my $future = $storage->txn_do_async(sub { ... }, @args);
Run a whole transaction in a single forked child. Mechanically identical to the CRUD path: the child runs the inherited sync storage's ordinary txn_do($body, @args) (core wraps BEGIN / body / COMMIT|ROLLBACK + retry in a BlockRunner), so BEGIN, the body and COMMIT/ROLLBACK all execute on the child's own freshly-reconnected sync connection. The $body closure does not cross the process boundary as data -- it is inherited through fork() from parent memory and runs in the child; it is called as $body->(@args) and receives no storage argument (it closes over $schema/$storage).
Two limits the caller must respect:
The body's return value must be Storable-serializable -- scalars or plain array/hash refs of scalars. It is frozen back over the pipe, so a live
Row/ResultSetobject (which drags its storage and live DB handle along) cannot cross: such a return surfaces as a failed Future with a clear message from "get" (see "_serialization_error"). Return plain data (ids, hashrefs of column values), not live result objects.The body must use sync operations only. Calling a
*_asyncmethod inside the body would fork again from within the child. Use the ordinary sync CRUD ($schema->resultset(...)->...) inside atxn_do_asyncbody.
ACTIVATION
Loading this module (or 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. A connection then opts in per-connection, at connect time:
my $schema = MyApp::Schema->connect($dsn, $user, $pass, { async => 'forked' });
Nothing is auto-wired: a connection opened without { async => 'forked' } stays fully synchronous (its *_async methods croak). When the mode is chosen, the core resolver in DBIO::Storage::DBI constructs __PACKAGE__->new($schema) as the embedded async backend and feeds it the sync storage's DBI-form connect info via "connect_info"; that backend then answers the six *_async methods.
STATUS
All six *_async entry points are implemented (with their sync ->get wrappers): each forks per query and roundtrips through "_run_forked" and DBIO::Forked::Future. "txn_do_async" runs the whole transaction in one child; see the two caller-facing limits documented on it.
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.