DBIx-QuickDB

 view release on metacpan or  search on metacpan

t/Pool/Pool.pm  view on Meta::CPAN

    my $stamp = check_cloned($db);
    $stamp += $delta;

    open(my $fh, '>', $db->dir . "/cloned") or die "$!";
    print $fh $stamp, "\n";
    1;
}

# Compare result rows by their test_val column (in order) without asserting the
# absolute test_id values. A PostgreSQL crash recovery -- e.g. after a slow host
# blew the watcher's shutdown grace period and the server was SIGKILLed --
# advances a SERIAL sequence by up to SEQ_LOG_VALS (32), so test_id is not
# deterministic across runs. We still require the ids to be present, positive,
# and strictly increasing in the requested order.
sub check_rows {
    my ($got, $expect_vals, $name) = @_;

    my @vals = map { $_->{test_val} } @$got;
    my $vals_ok = is(\@vals, $expect_vals, $name);

    my $ids_ok = 1;
    my $prev;
    for my $row (@$got) {
        my $id = $row->{test_id};
        unless (defined($id) && $id =~ /^\d+$/ && $id > 0 && (!defined($prev) || $id > $prev)) {
            $ids_ok = 0;
            last;
        }
        $prev = $id;
    }
    ok($ids_ok, "$name (test_ids present, positive, strictly increasing)")
        or diag("test_ids: " . join(', ', map { defined $_->{test_id} ? $_->{test_id} : 'undef' } @$got));

    return $vals_ok && $ids_ok;
}

# Diagnostics for the intermittent freebsd/PostgreSQL-9.3 failure where a clone
# that started fine is dead by the time we connect to it. Dumps everything we
# can about a db -- socket/pid liveness and, crucially, the server's own log --
# to STDERR so it shows up in CPAN smoke reports. Best-effort; never dies.
sub qdb_diag {
    my ($db, $label) = @_;
    return unless $db;

    my @out = ("==== QDB-DIAG [$label] pid=$$ ====");

    my $dir  = eval { $db->dir };
    push @out, "  dir: " . (defined $dir ? $dir : '?');

    my $sock = eval { $db->socket };
    push @out, "  socket: " . (defined $sock ? $sock : '?')
        . " exists=" . (defined $sock && -e $sock ? 1 : 0)
        . " is_socket=" . (defined $sock && -S $sock ? 1 : 0);

    push @out, "  started(): " . (eval { $db->started } ? 1 : 0);

    my $w = $db->{ +DBIx::QuickDB::Driver::WATCHER() };
    if ($w) {
        my $spid = eval { $w->server_pid };
        push @out, "  server_pid: " . (defined $spid ? $spid : '?')
            . " alive=" . (defined $spid && kill(0, $spid) ? 1 : 0);
    }
    else {
        push @out, "  watcher: none (object thinks it is stopped)";
    }

    my $log = eval { $db->error_log };
    if ($log && -f $log) {
        open(my $fh, '<', $log) or push(@out, "  error.log: open failed: $!");
        if ($fh) {
            my @l = <$fh>;
            close($fh);
            @l = @l[-25 .. -1] if @l > 25;
            push @out, "  --- $log (last " . scalar(@l) . " lines) ---";
            for my $line (@l) { chomp $line; push @out, "    | $line" }
        }
    }
    else {
        push @out, "  error.log: " . (defined $log ? "$log (missing)" : 'n/a');
    }

    my $ipcs = eval { `ipcs -m 2>/dev/null | wc -l` };
    my $ipss = eval { `ipcs -s 2>/dev/null | wc -l` };
    chomp($ipcs) if defined $ipcs;
    chomp($ipss) if defined $ipss;
    push @out, "  ipcs shmem-lines=" . (defined $ipcs ? $ipcs : '?')
        . " sem-lines=" . (defined $ipss ? $ipss : '?');

    print STDERR join("\n", @out), "\n";
    return;
}

# Wrap a connect; on failure dump diagnostics for the db, its sibling, and the
# sources it was cloned from, then rethrow so the test still fails.
sub diag_connect {
    my ($db, $label, @others) = @_;
    my $dbh;
    return $dbh if eval { $dbh = $db->connect(); 1 };
    my $err = $@;
    print STDERR "==== QDB-DIAG connect FAILED for [$label]: $err\n";
    qdb_diag($db, $label);
    qdb_diag($_->[1], $_->[0]) for @others;

    # Global snapshot: which postgres processes are alive (orphans?), and the
    # System V IPC objects in play -- the prime suspect for this 9.3 race.
    my $ps = eval { `ps -axww 2>/dev/null | grep '[p]ostgres'` };
    print STDERR "==== QDB-DIAG postgres processes ====\n", (length($ps // '') ? $ps : "  (none)\n");
    my $ipcs = eval { `ipcs -a 2>/dev/null` };
    print STDERR "==== QDB-DIAG ipcs -a ====\n", (length($ipcs // '') ? $ipcs : "  (none)\n");

    die $err;
}

ok($driver, "Got a driver ($driver)") or die "Cannot continue without a driver";

use Test2::Tools::QuickDB qw/skipall_on_resource_error/;
use DBIx::QuickDB::Pool cache_dir => tempdir(CLEANUP => 1), verbose => 0;

# db() that builds/clones a new server can fail on a host out of System V IPC at
# any point in the run, not just the very first build. Wrap the new-server
# allocations so such a host skips (environment limit) instead of failing; any
# other error is rethrown.
sub db_or_skip {
    my @out;
    my $ok = eval { @out = db(@_); 1 };
    return wantarray ? @out : $out[0] if $ok;
    my $err = $@;
    skipall_on_resource_error($err);
    die $err;
}
is(\@Test::Pool::EXPORT_OK, ['db'], "Added db to export_ok");

isa_ok(QDB_POOL(), [$CLASS], "We have access to the $CLASS instance");
can_ok(
    QDB_POOL(),
    [qw/library verbose set_verbose update_checksums set_update_checksums purge_old set_purge_old/],
    "Accessors are as expected"
);
is(QDB_POOL()->library, __PACKAGE__, "Set the library");

driver $driver => (
    driver_args => { $caller && $caller->can('DBD_DRIVER') ? (dbd_driver => $caller->DBD_DRIVER) : () },
    build => sub {
        my $class = shift;
        my ($db) = @_;

        $db->load_sql(quickdb => lc("t/schema/$driver.sql"));

        my $dbh = $db->connect;
        isa_ok($dbh, ['DBI::db'], "Connected");

        ok($dbh->do("INSERT INTO quick_test(test_val) VALUES('base')"), "Insert success");

        my $sth = $dbh->prepare('SELECT * FROM quick_test WHERE test_val = ?');
        $sth->execute('base');
        my $all = $sth->fetchall_arrayref({});
        check_rows($all, ['base'], "Got the inserted row");
    },
);

my $start = time();
# This is the first server built from scratch (initdb + start). On a smoke host
# already out of System V semaphores/shared memory it fails right here; treat
# that as a skip (environment limit), not a failure. Any other error is real.



( run in 0.672 second using v1.01-cache-2.11-cpan-df04353d9ac )