lib/PublicInbox/ExtSearch.pm | 10 ++++++++-- lib/PublicInbox/Gcf2.pm | 26 +++++++++++++++++++++++--- lib/PublicInbox/Git.pm | 16 +++++++++++++++- lib/PublicInbox/Inbox.pm | 11 +++++++++-- diff --git a/lib/PublicInbox/ExtSearch.pm b/lib/PublicInbox/ExtSearch.pm index bd301158a6b0a4163cb58073384bf034a5238dda..cd7cba2e88ce217bbc036a1655f479b291304efe 100644 --- a/lib/PublicInbox/ExtSearch.pm +++ b/lib/PublicInbox/ExtSearch.pm @@ -33,12 +33,18 @@ # same as per-inbox ->over, for now... sub over { my ($self) = @_; - $self->{over} //= PublicInbox::Over->new("$self->{xpfx}/over.sqlite3"); + $self->{over} //= do { + PublicInbox::Inbox::_cleanup_later($self); + PublicInbox::Over->new("$self->{xpfx}/over.sqlite3"); + }; } sub git { my ($self) = @_; - $self->{git} //= PublicInbox::Git->new("$self->{topdir}/ALL.git"); + $self->{git} //= do { + PublicInbox::Inbox::_cleanup_later($self); + PublicInbox::Git->new("$self->{topdir}/ALL.git"); + }; } # returns a hashref of { $NEWSGROUP_NAME => $ART_NO } using the `xref3' table diff --git a/lib/PublicInbox/Gcf2.pm b/lib/PublicInbox/Gcf2.pm index 64945ca69cff7fa3cec96539fad0ac0a8f9bfe9d..f546208fa4adeab33016619575e16d9e2b4acb47 100644 --- a/lib/PublicInbox/Gcf2.pm +++ b/lib/PublicInbox/Gcf2.pm @@ -8,6 +8,7 @@ use strict; use v5.10.1; use PublicInbox::Spawn qw(which popen_rd); # may set PERL_INLINE_DIRECTORY use Fcntl qw(LOCK_EX SEEK_SET); +use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC); use IO::Handle; # autoflush BEGIN { my (%CFG, $c_src); @@ -96,11 +97,21 @@ $gcf2->add_alternate($objdir); 1; } -# Usage: $^X -MPublicInbox::Gcf2 -e PublicInbox::Gcf2::loop +sub have_unlinked_files () { + # FIXME: port gcf2-like over to git.git so we won't need to + # deal with libgit2 + return 1 if $^O ne 'linux'; + open my $fh, '<', "/proc/$$/maps" or return; + while (<$fh>) { return 1 if /\.(?:idx|pack) \(deleted\)$/ } + undef; +} + +# Usage: $^X -MPublicInbox::Gcf2 -e PublicInbox::Gcf2::loop [EXPIRE-TIMEOUT] # (see lib/PublicInbox/Gcf2Client.pm) -sub loop () { +sub loop (;$) { + my $exp = $_[0] || $ARGV[0] || 60; # seconds my $gcf2 = new(); - my %seen; + my (%seen, $check_at); STDERR->autoflush(1); STDOUT->autoflush(1); @@ -116,12 +127,21 @@ warn "I: $$ $oid missing, retrying in $git_dir\n"; $gcf2 = new(); %seen = ($git_dir => add_alt($gcf2,"$git_dir/objects")); + $check_at = clock_gettime(CLOCK_MONOTONIC) + $exp; if ($gcf2->cat_oid(1, $oid)) { warn "I: $$ $oid found after retry\n"; } else { warn "W: $$ $oid missing after retry\n"; print "$oid missing\n"; # mimic git-cat-file + } + } else { # check expiry to deal with deleted pack files + my $now = clock_gettime(CLOCK_MONOTONIC); + $check_at //= $now + $exp; + if ($now > $check_at && have_unlinked_files()) { + undef $check_at; + $gcf2 = new(); + %seen = (); } } } diff --git a/lib/PublicInbox/Git.pm b/lib/PublicInbox/Git.pm index 5ef1db2f8c5da5fd94bf70e57255173f0ba16e3c..cf51239f1906c3863058198e7a175514f289b3b8 100644 --- a/lib/PublicInbox/Git.pm +++ b/lib/PublicInbox/Git.pm @@ -12,7 +12,7 @@ use v5.10.1; use parent qw(Exporter); use POSIX (); use IO::Handle; # ->autoflush -use Errno qw(EINTR EAGAIN); +use Errno qw(EINTR EAGAIN ENOENT); use File::Glob qw(bsd_glob GLOB_NOSORT); use File::Spec (); use Time::HiRes qw(stat); @@ -521,6 +521,20 @@ $ent->{reference} = $ref; } } $ent; +} + +sub cleanup_if_unlinked { + my ($self) = @_; + return cleanup($self) if $^O ne 'linux'; + # Linux-specific /proc/$PID/maps access + # TODO: support this inside git.git + for my $fld (qw(pid pid_c)) { + my $pid = $self->{$fld} // next; + open my $fh, '<', "/proc/$pid/maps" or next; + while (<$fh>) { + return cleanup($self) if /\.(?:idx|pack) \(deleted\)$/; + } + } } 1; diff --git a/lib/PublicInbox/Inbox.pm b/lib/PublicInbox/Inbox.pm index 20f8c88489fcb8057e43817eef238c5e9fe9e3a8..02ffc7be3bef74e1d4c8738856c59f8f89c5d811 100644 --- a/lib/PublicInbox/Inbox.pm +++ b/lib/PublicInbox/Inbox.pm @@ -22,8 +22,15 @@ my $CLEANUP = {}; # string(inbox) -> inbox sub git_cleanup ($) { my ($self) = @_; - my $git = $self->{git} or return; - $git->cleanup; + if ($self->isa(__PACKAGE__)) { + # normal Inbox; low startup cost, and likely to have many + # many so this keeps process/pipe counts in check + $self->{git}->cleanup if $self->{git}; + } else { + # ExtSearch, high startup cost if ->ALL, and probably + # only one per-daemon, so teardown only if required: + $self->git->cleanup_if_unlinked; + } } sub cleanup_task () {