DBIx-QuickDB
view release on metacpan or search on metacpan
lib/DBIx/QuickDB/Pool.pm view on Meta::CPAN
};
sub import {
my $class = shift;
my %params = @_;
$params{library} ||= caller;
my $inst = $class->new(%params);
$inst->export();
}
sub init {
my $self = shift;
croak "'cache_dir' is a required_attribute" unless $self->{+CACHE_DIR};
croak "'cache_dir' must point to an existing directory"
unless -d $self->{+CACHE_DIR};
croak "'$self->{+INSTANCE_DIR}' must be an existing directory"
if $self->{+INSTANCE_DIR} && !-d $self->{+INSTANCE_DIR};
$self->{+LIBRARY} //= caller(1);
$self->{+VERBOSE} //= 0;
$self->{+PURGE_OLD} //= 0;
$self->{+UPDATE_CHECKSUMS} //= 1;
$self->{+DATABASES} //= {};
}
# Remove a disposable database tree, or move it out of the canonical cache path
# when Windows still refuses the removal after retries. Returning true means the
# canonical path is clear and safe to rebuild; clear_old_cache() reclaims the
# quarantine on a later pass, and it can never be mistaken for a valid entry.
sub _remove_or_quarantine {
my ($self, $dir) = @_;
my $stale = remove_tree_or_quarantine($dir);
return 0 unless defined $stale;
$self->diag("$$ Quarantined stale database directory '$dir' as '$stale'")
if length $stale;
return 1;
}
sub clear_old_cache {
my $self = shift;
my ($age) = @_;
my $dir = $self->{+CACHE_DIR};
opendir(my $dh, $dir) or die "Could not open cache dir '$dir': $!";
for my $name (readdir($dh)) {
my $full = "$dir/$name";
# Quarantines sit outside the canonical cache path (see
# _remove_or_quarantine). Retry their removal on every pass: the
# transient Windows file lock that blocked deletion may have cleared.
if ($name =~ m/\.STALE-/) {
remove_tree_robust($full) if -d $full;
next;
}
# A live cache entry is a bare directory name. Its marker files
# ("$name.lock", "$name.READY") do contain dots and are handled
# through their entry, never scanned directly.
next if $name =~ m/\./;
next unless -d $full;
my $file = "$full/cloned";
next unless -f $file;
open(my $fh, '<', $file) or next;
my $stamp = <$fh>;
close($fh);
$stamp = '' unless defined $stamp;
chomp($stamp);
# Time::HiRes values written by older releases can contain a decimal
# comma under the Windows process locale (for example 1785633742,27439).
# Cache expiry needs only whole seconds, so accept either historical
# fractional spelling and reduce it to a locale-independent integer. A
# damaged stamp is treated as infinitely old so it is reclaimed rather
# than pinning an unusable clone in the cache forever.
$stamp =~ s/[.,]\d+\z//;
$stamp = 0 unless $stamp =~ /^\d+\z/;
next unless $age <= (time - $stamp);
eval {
# An expired SQLite source may still have DBI handles retained by
# application/test code. On Windows those handles prevent unlinking
# the database file. Close every matching handle before removal.
disconnect_dbi_handles($full);
# If removal remains blocked, moving the tree out of its canonical
# path keeps a later build from seeing partially-removed SQLite
# schema/data. Either way, invalidate the external markers: expiry
# was explicitly requested, and build_db() will refuse to build on
# any stubborn tree that could not be quarantined.
$self->_remove_or_quarantine($full);
unlink("$full.lock") if -e "$full.lock";
unlink("$full.READY") if -e "$full.READY";
1;
} or warn $@;
}
}
sub export {
my $self = shift;
my $library = $self->{+LIBRARY};
my $qdb = sub { $self };
my $build = sub { $self->add_db(@_, caller => [caller()]) };
my $db = sub { $self->fetch_db(@_, caller => [caller()]) };
my $driver = sub { $self->add_driver(@_, caller => [caller()]) };
no strict 'refs';
*{"$library\::QDB_POOL"} = $qdb;
*{"$library\::driver"} = $driver;
*{"$library\::build"} = $build;
*{"$library\::db"} = $db;
push @{"$library\::EXPORT_OK"} => 'db';
}
sub throw {
my $self = shift;
my ($msg, %params) = @_;
my $caller = $params{caller} || [caller(1)];
die "$msg at $caller->[1] line $caller->[2].\n";
}
sub alert {
my $self = shift;
my ($msg, %params) = @_;
my $caller = $params{caller} || [caller(1)];
warn "$msg at $caller->[1] line $caller->[2].\n";
}
sub diag {
my $self = shift;
my ($msg, %params) = @_;
my $show = $self->{+VERBOSE} || $self->{+SHOW_DIAG};
return unless $show;
# Only append caller info when asked
if (my $caller = $params{caller}) {
$msg .= " at $caller->[1] line $caller->[2].";
}
if ($show > 1) {
print STDERR "$msg\n";
}
else {
( run in 1.921 second using v1.01-cache-2.11-cpan-800906f7e73 )