]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Xapcmd.pm
No ext_urls
[public-inbox.git] / lib / PublicInbox / Xapcmd.pm
index 8b8958c71ffbc6749427215664469eab2ae1378e..106856369c689007470b7ec364796e29079821f6 100644 (file)
@@ -1,14 +1,14 @@
-# Copyright (C) 2018-2021 all contributors <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 package PublicInbox::Xapcmd;
 use strict;
-use PublicInbox::Spawn qw(which popen_rd nodatacow_dir);
+use PublicInbox::Spawn qw(which popen_rd);
+use PublicInbox::Syscall;
 use PublicInbox::Admin qw(setup_signals);
 use PublicInbox::Over;
 use PublicInbox::SearchIdx;
 use File::Temp 0.19 (); # ->newdir
 use File::Path qw(remove_tree);
-use File::Basename qw(dirname);
 use POSIX qw(WNOHANG _exit);
 
 # support testing with dev versions of Xapian which installs
@@ -21,13 +21,25 @@ sub commit_changes ($$$$) {
        my $reshard = $opt->{reshard};
 
        $SIG{INT} or die 'BUG: $SIG{INT} not handled';
-       my @old_shard;
-       my $over_chg;
-
-       while (my ($old, $newdir) = each %$tmp) {
+       my (@old_shard, $over_chg);
+
+       # Sort shards highest-to-lowest, since ->xdb_shards_flat
+       # determines the number of shards to load based on the max;
+       # and we'd rather xdb_shards_flat to momentarily fail rather
+       # than load out-of-date shards
+       my @order = sort {
+               my ($x) = ($a =~ m!/([0-9]+)/*\z!);
+               my ($y) = ($b =~ m!/([0-9]+)/*\z!);
+               ($y // -1) <=> ($x // -1) # we may have non-shards
+       } keys %$tmp;
+
+       my ($dname) = ($order[0] =~ m!(.*/)[^/]+/*\z!);
+       my $mode = (stat($dname))[2];
+       for my $old (@order) {
                next if $old eq ''; # no invalid paths
-               my @st = stat($old);
-               if (!@st && !defined($opt->{reshard})) {
+               my $newdir = $tmp->{$old};
+               my $have_old = -e $old;
+               if (!$have_old && !defined($opt->{reshard})) {
                        die "failed to stat($old): $!";
                }
 
@@ -47,17 +59,13 @@ sub commit_changes ($$$$) {
                        next;
                }
 
-               if (@st) {
-                       chmod($st[2] & 07777, $new) or die "chmod $old: $!\n";
+               chmod($mode & 07777, $new) or die "chmod($new): $!\n";
+               if ($have_old) {
                        rename($old, "$new/old") or
                                        die "rename $old => $new/old: $!\n";
                }
                rename($new, $old) or die "rename $new => $old: $!\n";
-               if (@st) {
-                       my $prev = "$old/old";
-                       remove_tree($prev) or
-                               die "failed to remove $prev: $!\n";
-               }
+               push @old_shard, "$old/old" if $have_old;
        }
 
        # trigger ->check_inodes in read-only daemons
@@ -150,7 +158,7 @@ sub process_queue {
 
        # run in parallel:
        my %pids;
-       local %SIG = %SIG;
+       local @SIG{keys %SIG} = values %SIG;
        setup_signals(\&kill_pids, \%pids);
        while (@$queue) {
                while (scalar(keys(%pids)) < $max && scalar(@$queue)) {
@@ -199,12 +207,12 @@ sub prepare_run {
                        warn
 "--reshard=$reshard ignored for v1 $ibx->{inboxdir}\n";
                }
-               my $dir = dirname($old);
+               my ($dir) = ($old =~ m!(.*?/)[^/]+/*\z!);
                same_fs_or_die($dir, $old);
                my $v = PublicInbox::Search::SCHEMA_VERSION();
                my $wip = File::Temp->newdir("xapian$v-XXXX", DIR => $dir);
                $tmp->{$old} = $wip;
-               nodatacow_dir($wip->dirname);
+               PublicInbox::Syscall::nodatacow_dir($wip->dirname);
                push @queue, [ $old, $wip ];
        } elsif (defined $old) {
                opendir my $dh, $old or die "Failed to opendir $old: $!\n";
@@ -235,7 +243,7 @@ sub prepare_run {
                        same_fs_or_die($old, $wip->dirname);
                        my $cur = "$old/$dn";
                        push @queue, [ $src // $cur , $wip ];
-                       nodatacow_dir($wip->dirname);
+                       PublicInbox::Syscall::nodatacow_dir($wip->dirname);
                        $tmp->{$cur} = $wip;
                }
                # mark old shards to be unlinked
@@ -286,7 +294,7 @@ sub run {
                PublicInbox::SearchIdx::load_xapian_writable();
        }
 
-       local %SIG = %SIG;
+       local @SIG{keys %SIG} = values %SIG;
        setup_signals();
        $ibx->with_umask(\&_run, $ibx, $cb, $opt);
 }
@@ -344,7 +352,7 @@ sub compact ($$) { # cb_spawn callback
        $pr->("$pfx `".join(' ', @$cmd)."'\n") if $pr;
        push @$cmd, $src, $dst;
        my ($rd, $pid);
-       local %SIG = %SIG;
+       local @SIG{keys %SIG} = values %SIG;
        setup_signals(\&kill_compact, \$pid);
        ($rd, $pid) = popen_rd($cmd, undef, $rdr);
        while (<$rd>) {
@@ -429,14 +437,14 @@ sub cpdb ($$) { # cb_spawn callback
        }
 
        my ($tmp, $ft);
-       local %SIG = %SIG;
+       local @SIG{keys %SIG} = values %SIG;
        if ($opt->{compact}) {
-               my $dir = dirname($new);
+               my ($dir) = ($new =~ m!(.*?/)[^/]+/*\z!);
                same_fs_or_die($dir, $new);
                $ft = File::Temp->newdir("$new.compact-XXXX", DIR => $dir);
                setup_signals();
                $tmp = $ft->dirname;
-               nodatacow_dir($tmp);
+               PublicInbox::Syscall::nodatacow_dir($tmp);
        } else {
                $tmp = $new;
        }