]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
lei_mail_sync: cleanup stale/dangling fids if possible
authorEric Wong <e@80x24.org>
Wed, 12 Apr 2023 06:19:10 +0000 (06:19 +0000)
committerEric Wong <e@80x24.org>
Thu, 13 Apr 2023 11:28:22 +0000 (11:28 +0000)
I'm not sure how it happens or if/when it was fixed, but my
earliest lei installations have hit some
"E: fid=$fid for $oidhex unknown" messages on `lei import'
invocations.

This really should've enabled the foreign keys pragma to begin
with; but we'll probably start using that in the future.  For
now, at least rely on a transaction to keep things consistent
in SQLite.

lib/PublicInbox/LeiMailSync.pm

index 665206a8319ec661501666d139ba0f2413f607e1..ea4d48c1a124c324b71d2a12da7c9fa5563581cb 100644 (file)
@@ -339,6 +339,17 @@ SELECT $op(uid) FROM blob2num WHERE fid = ?
        $ret;
 }
 
+# must be called with lock
+sub _forget_fids ($;@) {
+       my $dbh = shift;
+       $dbh->begin_work;
+       for my $t (qw(blob2name blob2num folders)) {
+               my $sth = $dbh->prepare_cached("DELETE FROM $t WHERE fid = ?");
+               $sth->execute($_) for @_;
+       }
+       $dbh->commit;
+}
+
 # returns a { location => [ list-of-ids-or-names ] } mapping
 sub locations_for {
        my ($self, $oidbin) = @_;
@@ -379,18 +390,28 @@ sub locations_for {
 
        $sth = $dbh->prepare('SELECT loc FROM folders WHERE fid = ? LIMIT 1');
        my $ret = {};
+       my $drop_fids = $dbh->{ReadOnly} ? undef : {};
        while (my ($fid, $ids) = each %fid2id) {
                $sth->execute($fid);
                my ($loc) = $sth->fetchrow_array;
                unless (defined $loc) {
+                       my $del = '';
+                       if ($drop_fids) {
+                               $del = ' (deleting)';
+                               $drop_fids->{$fid} = $fid;
+                       }
                        my $oidhex = unpack('H*', $oidbin);
-                       warn "E: fid=$fid for $oidhex unknown:\n", map {
-                                       'E: '.(ref() ? $$_ : "#$_")."\n";
+                       warn "E: fid=$fid for $oidhex stale/unknown:\n", map {
+                                       'E: '.(ref() ? $$_ : "#$_")."$del\n";
                                } @$ids;
                        next;
                }
                $ret->{$loc} = $ids;
        }
+       if ($drop_fids && scalar(values %$drop_fids)) {
+               my $lk = $self->lock_for_scope;
+               _forget_fids($self->{dbh}, values %$drop_fids);
+       }
        scalar(keys %$ret) ? $ret : undef;
 }
 
@@ -596,14 +617,10 @@ EOF
 sub forget_folders {
        my ($self, @folders) = @_;
        my $lk = $self->lock_for_scope;
-       for my $folder (@folders) {
-               my $fid = delete($self->{fmap}->{$folder}) //
-                       fid_for($self, $folder) // next;
-               for my $t (qw(blob2name blob2num folders)) {
-                       $self->{dbh}->do("DELETE FROM $t WHERE fid = ?",
-                                       undef, $fid);
-               }
-       }
+       _forget_fids($self->{dbh}, map {
+               delete($self->{fmap}->{$_}) //
+                       fid_for($self, $_) // ();
+       } @folders);
 }
 
 # only used for changing canonicalization errors