DBIO-PostgreSQL-EV
view release on metacpan or search on metacpan
t/17-concurrency-live.t view on Meta::CPAN
# starvation ("one conn preferred, the rest starve") is exactly the karr #13
# bug the shift-fix cured; verified empirically by running this file against
# the still-installed pop core (RED) vs. the fixed shift core (GREEN).
#
# ($rot_pids[i] == $rot_pids[i-4] alone is NOT a discriminator: if all pids
# are identical it holds trivially. It is the POSITIVE FIFO signature only
# once (B1) has established that there really are 4 distinct pids.)
my @rot_pids;
my $slots = $storage->pool->max_size; # 4
for my $iter (1 .. 2 * $slots) { # 2 full rounds = 8 iterations
# WHERE id=1 pins the result to exactly one row; the sole selected item is
# the backend pid (index 0). Awaiting here (not batching) is the whole
# point â it forces the pool fully idle before the next acquire, so acquire
# ORDER, not fan-out, is what is under test.
my $f = $storage->select_async($table, [ \'pg_backend_pid()' ], { id => 1 });
await_guarded($f, "FIFO rotation iter $iter");
my @rows = $f->get;
is scalar(@rows), 1, "rotation iter $iter: exactly one row";
push @rot_pids, $rows[0][0];
is $storage->pool->size, 4, "rotation iter $iter: pool still 4 (churn, no growth)";
}
# (B1) FIFO rotates through EVERY pooled backend â the primary LIFO
# discriminator. Under pop this set collapses to size 1.
my %rot_seen;
$rot_seen{$_}++ for @rot_pids;
is scalar(keys %rot_seen), $slots,
"sequential churn visited all $slots distinct backends (FIFO rotation; LIFO would stick to 1)";
# (B2) No two CONSECUTIVE iterations reused the same backend: shift always
# hands out a different conn than the one release just pushed to the back.
# Under LIFO every consecutive pair is identical.
my $consecutive_repeats = 0;
for my $i (1 .. $#rot_pids) {
$consecutive_repeats++ if $rot_pids[$i] == $rot_pids[$i - 1];
}
is $consecutive_repeats, 0,
'no consecutive iteration reused the same backend (FIFO hands out a fresh conn; LIFO repeats every time)';
# (B3) Positive FIFO signature: iteration i reuses the SAME backend as
t/19-copy-in-regression.t view on Meta::CPAN
# and the Storage's CRUD Future fails.
#
# The fix (Storage.pm:copy_in) waits for EV::Pg's SECOND callback firing
# (the one that delivers the final cmd_tuples after libpq has actually
# finished the COPY) before releasing the connection. This test would
# fail loudly against the original implementation and pass against the
# fixed one. It runs the cycle THREE times back-to-back because the bug
# is timing-sensitive: the pool typically has 5 idle slots, so the first
# copy_in is usually followed by an acquire that picks a DIFFERENT idle
# connection; the bug surfaces once the same Storage cycles the same
# physical connection. Three iterations exercise both code paths.
#
# Pre-fix behavior on this test:
# - iteration 1: first select_async after copy_in picks a different
# idle connection (or, if pool_size == 1, hits the still-busy one);
# not always a hard fail, so we run it three times.
# - iterations 2+3: same connection gets reused, libpq returns
# "another command is already in progress", select_async Future fails.
#
# Post-fix behavior:
# - every iteration's follow-up select_async succeeds and returns the
# rows that were just COPY-loaded.
BEGIN {
plan skip_all => 'Set DBIO_TEST_PG_DSN to run integration tests'
unless $ENV{DBIO_TEST_PG_DSN};
}
( run in 1.754 second using v1.01-cache-2.11-cpan-f03e8824b8d )