diff options
Diffstat (limited to 'lib/PublicInbox/LeiALE.pm')
-rw-r--r-- | lib/PublicInbox/LeiALE.pm | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/lib/PublicInbox/LeiALE.pm b/lib/PublicInbox/LeiALE.pm new file mode 100644 index 00000000..ce03f5b4 --- /dev/null +++ b/lib/PublicInbox/LeiALE.pm @@ -0,0 +1,106 @@ +# Copyright (C) all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> + +# All Locals Ever: track lei/store + externals ever used as +# long as they're on an accessible FS. Includes "lei q" --include +# and --only targets that haven't been through "lei add-external". +# Typically: ~/.cache/lei/all_locals_ever.git +package PublicInbox::LeiALE; +use v5.12; +use parent qw(PublicInbox::LeiSearch PublicInbox::Lock); +use PublicInbox::Git; +use autodie qw(close open rename seek truncate); +use PublicInbox::Import; +use PublicInbox::OnDestroy; +use PublicInbox::LeiXSearch; +use Fcntl qw(SEEK_SET); + +sub _new { + my ($d) = @_; + PublicInbox::Import::init_bare($d, 'ale'); + bless { + git => PublicInbox::Git->new($d), + lock_path => "$d/lei_ale.state", # dual-duty lock + state + ibxish => [], # Inbox and ExtSearch (and LeiSearch) objects + }, __PACKAGE__ +} + +sub new { + my ($self, $lei) = @_; + ref($self) or $self = _new($lei->cache_dir . '/all_locals_ever.git'); + my $lxs = PublicInbox::LeiXSearch->new; + my $sto = $lei->_lei_store; + $lxs->prepare_external($sto->search) if $sto; + for my $loc ($lei->externals_each) { # locals only + $lxs->prepare_external($loc) if -d $loc; + } + $self->refresh_externals($lxs, $lei); + $self; +} + +sub over {} # undef for xoids_for + +sub overs_all { # for xoids_for (called only in lei workers?) + my ($self) = @_; + my $fgen = $PublicInbox::OnDestroy::fork_gen ; + if (($self->{fgen} // $fgen) != $fgen) { + delete($_->{over}) for @{$self->{ibxish}}; + } + $self->{fgen} = $fgen; + grep(defined, map { $_->over } @{$self->{ibxish}}); +} + +sub refresh_externals { + my ($self, $lxs, $lei) = @_; + $self->git->cleanup; + my $lk = $self->lock_for_scope; + my $cur_lxs = ref($lxs)->new; + my $orig = PublicInbox::IO::read_all $self->{lockfh}; + my $new = ''; + my $old = ''; + my $gone = 0; + my %seen_ibxish; # $dir => any-defined value + for my $dir (split(/\n/, $orig)) { + if (-d $dir && -r _ && $cur_lxs->prepare_external($dir)) { + $seen_ibxish{$dir} //= length($old .= "$dir\n"); + } else { + ++$gone; + } + } + my @ibxish = $cur_lxs->locals; + for my $x ($lxs->locals) { + my $d = $lei->canonpath_harder($x->{inboxdir} // $x->{topdir}); + $seen_ibxish{$d} //= do { + $new .= "$d\n"; + push @ibxish, $x; + }; + } + if ($new ne '' || $gone) { + $self->{lockfh}->autoflush(1); + if ($gone) { + seek($self->{lockfh}, 0, SEEK_SET); + truncate($self->{lockfh}, 0); + } else { + $old = ''; + } + print { $self->{lockfh} } $old, $new or die "print: $!"; + } + $new = ''; + my $f = $self->git->{git_dir}.'/objects/info/alternates'; + $old = PublicInbox::IO::try_cat $f; + for my $x (@ibxish) { + $new .= $lei->canonpath_harder($x->git->{git_dir})."/objects\n"; + } + $self->{ibxish} = \@ibxish; + return if $old eq $new; + + # this needs to be atomic since child processes may start + # git-cat-file at any time + my $tmp = "$f.$$.tmp"; + open my $fh, '>', $tmp; + print $fh $new; + close $fh; + rename($tmp, $f) +} + +1; |