DBIx-Loop
view release on metacpan or search on metacpan
0.03 2026-08-12
- t/06-hyperman.t failed the distribution on a box carrying a
Hyperman older than 0.10.
0.02 2026-08-07
- Fix a pool worker dying while idle taking the whole pool down
with it. Worker death was only ever noticed as EOF on the read
side, and an idle worker's fd is not readable, so the first
thing that learned about it was the write of the next request -
which failed the future and left the slot alive with a valid
fd.
- A public C ABI. include/dbil_abi.h declares a versioned function-
pointer table.
- selectall_rowhash: every row as a hashref, in the order the server
returned them - DBI's selectall_arrayref($sql, { Slice => {} }).
selectall_hashref cannot stand in for it, because keying rows by a
column destroys the ordering that keyset pagination depends on.
t/17-rowhash.t.
- Fork safety. A pool belongs to the process that forked its workers.
Without that, a program that forked after its first statement (any
include/dbil_pool.h view on Meta::CPAN
if (dbil_write_frame(aTHX_ w->fd, bytes) >= 0) return;
/* The write failed, so the frame did not arrive whole and the statement
* provably never ran: dbil_write_frame only returns < 0 having written
* less than all of it.
*
* What matters here is the SLOT, not this one request. A worker killed
* while idle is invisible until something is written to it - death is
* otherwise only ever noticed as EOF on the read side, and an idle
* worker's fd is not readable - so this is the first and only moment we
* learn about it. Leaving the slot alive meant dbil_pool_run kept
* choosing it, being the lowest-numbered idle worker with a live fd, and
* failed every future from then on while the healthy workers sat idle.
* Treat a failed write as the death notice it is. */
reserved = w->reserved;
if (!reserved) {
/* Nothing executed, so this is a retry and not a loss. Put it back at
* the head of the queue so it keeps its place, and let the respawn
* inside worker_died pick it up. */
SvREFCNT_dec(w->future);
t/15-fork.t view on Meta::CPAN
}
# ---- the parent survives the child's exit -----------------------------------
# This is the SIGTERM bug: the child's DESTROY used to reap the parent's
# workers, so the next parent query hung or failed.
{
my @still = $db->_worker_pids;
is_deeply([sort { $a <=> $b } @still], [sort { $a <=> $b } @parent_pids],
'the parent still has the same workers after the child exited');
my $alive = grep { kill(0, $_) } @still;
is($alive, scalar @still, 'and every one of them is still running');
my $r = await1($db->query("SELECT v FROM t WHERE id = 1"));
is($r->{rows}[0][0], 'parent', 'the parent can still query');
}
# ---- no response theft under concurrent children ----------------------------
# Each child writes a row only it knows, then reads it back. If two processes
# were sharing a socketpair, a child would sometimes receive a sibling's row.
{
await1($db->do("INSERT INTO t (id, v) VALUES (?, ?)", $_ + 10, "child$_"))
( run in 3.843 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )