about summary refs log tree commit homepage
path: root/lib/PublicInbox/LeiALE.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicInbox/LeiALE.pm')
-rw-r--r--lib/PublicInbox/LeiALE.pm106
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;