}
}
-sub index_users ($$) {
+sub index_headers ($$) {
my ($self, $smsg) = @_;
-
- my $from = $smsg->from;
- my $to = $smsg->to;
- my $cc = $smsg->cc;
-
- index_text($self, $from, 1, 'A'); # A - author
- index_text($self, $to, 1, 'XTO') if $to ne '';
- index_text($self, $cc, 1, 'XCC') if $cc ne '';
+ my @x = (from => 'A', # Author
+ subject => 'S', to => 'XTO', cc => 'XCC');
+ while (my ($field, $pfx) = splice(@x, 0, 2)) {
+ my $val = $smsg->{$field};
+ index_text($self, $val, 1, $pfx) if $val ne '';
+ }
}
sub index_diff_inc ($$$$) {
if ($part->{is_submsg}) {
my $mids = mids_for_index($part);
index_ids($self, $doc, $part, $mids);
- my $smsg = PublicInbox::Smsg->new($part);
- index_users($self, $smsg);
- index_text($self, $smsg->subject, 1, 'S') if $smsg->subject;
+ my $smsg = bless {}, 'PublicInbox::Smsg';
+ $smsg->populate($part);
+ index_headers($self, $smsg);
}
my ($s, undef) = msg_part_text($part, $ct);
sub add_xapian ($$$$) {
my ($self, $mime, $smsg, $mids) = @_;
- $smsg->{mime} = $mime; # XXX dangerous
my $hdr = $mime->header_obj;
my $doc = $X->{Document}->new;
- my $subj = $smsg->subject;
add_val($doc, PublicInbox::Search::TS(), $smsg->{ts});
my @ds = gmtime($smsg->{ds});
my $yyyymmdd = strftime('%Y%m%d', @ds);
add_val($doc, PublicInbox::Search::YYYYMMDD(), $yyyymmdd);
my $dt = strftime('%Y%m%d%H%M%S', @ds);
add_val($doc, PublicInbox::Search::DT(), $dt);
+ add_val($doc, PublicInbox::Search::BYTES(), $smsg->{bytes});
+ add_val($doc, PublicInbox::Search::UID(), $smsg->{num});
my $tg = term_generator($self);
$tg->set_document($doc);
- index_text($self, $subj, 1, 'S') if $subj;
- index_users($self, $smsg);
+ index_headers($self, $smsg);
msg_iter($mime, \&index_xapian, [ $self, $doc ]);
index_ids($self, $doc, $hdr, $mids);
sub add_message {
# mime = PublicInbox::Eml or Email::MIME object
- my ($self, $mime, $smsg) = @_;
+ my ($self, $mime, $smsg, $sync) = @_;
my $hdr = $mime->header_obj;
my $mids = mids_for_index($hdr);
$smsg //= bless { blob => '' }, 'PublicInbox::Smsg'; # test-only compat
};
# v1 and tests only:
- $smsg->{ds} //= msg_datestamp($hdr, $self->{autime});
- $smsg->{ts} //= msg_timestamp($hdr, $self->{cotime});
+ $smsg->populate($hdr, $sync);
+ $smsg->{bytes} //= length($mime->as_string);
eval {
# order matters, overview stores every possible piece of
for (; $head != $tail; $head++) {
my $docid = $head->get_docid;
my $doc = $db->get_document($docid);
- my $smsg = PublicInbox::Smsg->wrap($mid);
+ my $smsg = bless { mid => $mid }, 'PublicInbox::Smsg';
$smsg->load_expand($doc);
if ($smsg->{blob} eq $oid) {
push(@delete, $docid);
$self->{mm}->mid_delete(mid_mime($mime));
}
-sub index_both {
- my ($self, $mime, $smsg) = @_;
- my $num = index_mm($self, $mime);
- $smsg->{num} = $num;
- add_message($self, $mime, $smsg);
+# returns the number of bytes to add if given a non-CRLF arg
+sub crlf_adjust ($) {
+ if (index($_[0], "\r\n") < 0) {
+ # common case is LF-only, every \n needs an \r;
+ # so favor a cheap tr// over an expensive m//g
+ $_[0] =~ tr/\n/\n/;
+ } else { # count number of '\n' w/o '\r', expensive:
+ scalar(my @n = ($_[0] =~ m/(?<!\r)\n/g));
+ }
}
-sub unindex_both {
- my ($self, $mime) = @_;
- unindex_blob($self, $mime);
- unindex_mm($self, $mime);
+sub index_both { # git->cat_async callback
+ my ($bref, $oid, $type, $size, $sync) = @_;
+ my ($nr, $max) = @$sync{qw(nr max)};
+ ++$$nr;
+ $$max -= $size;
+ $size += crlf_adjust($$bref);
+ my $smsg = bless { bytes => $size, blob => $oid }, 'PublicInbox::Smsg';
+ my $self = $sync->{sidx};
+ my $eml = PublicInbox::Eml->new($bref);
+ my $num = index_mm($self, $eml);
+ $smsg->{num} = $num;
+ add_message($self, $eml, $smsg, $sync);
}
-sub do_cat_mail {
- my ($git, $blob, $sizeref) = @_;
- my $str = $git->cat_file($blob, $sizeref) or
- die "BUG: $blob not found in $git->{git_dir}";
- PublicInbox::Eml->new($str);
+sub unindex_both { # git->cat_async callback
+ my ($bref, $oid, $type, $size, $self) = @_;
+ my $eml = PublicInbox::Eml->new($bref);
+ unindex_blob($self, $eml);
+ unindex_mm($self, $eml);
}
# called by public-inbox-index
$self->{-inbox}->with_umask(sub { $self->_index_sync($opts) })
}
-sub batch_adjust ($$$$$) {
- my ($max, $bytes, $batch_cb, $latest, $nr) = @_;
- $$max -= $bytes;
- if ($$max <= 0) {
- $$max = $BATCH_BYTES;
- $batch_cb->($nr, $latest);
- }
-}
-
sub too_big ($$$) {
my ($self, $git, $oid) = @_;
my $max_size = $self->{index_max_size} or return;
# only for v1
sub read_log {
- my ($self, $log, $add_cb, $del_cb, $batch_cb) = @_;
+ my ($self, $log, $batch_cb) = @_;
my $hex = '[a-f0-9]';
my $h40 = $hex .'{40}';
my $addmsg = qr!^:000000 100644 \S+ ($h40) A\t${hex}{2}/${hex}{38}$!;
my $delmsg = qr!^:100644 000000 ($h40) \S+ D\t${hex}{2}/${hex}{38}$!;
my $git = $self->{git};
my $latest;
- my $bytes;
my $max = $BATCH_BYTES;
local $/ = "\n";
my %D;
my $line;
my $newest;
my $nr = 0;
+ my $sync = { sidx => $self, nr => \$nr, max => \$max };
while (defined($line = <$log>)) {
if ($line =~ /$addmsg/o) {
my $blob = $1;
if (delete $D{$blob}) {
+ # make sure pending index writes are done
+ # before writing to ->mm
+ $git->cat_async_wait;
+
if (defined $self->{regen_down}) {
my $num = $self->{regen_down}--;
$self->{mm}->num_highwater($num);
next;
}
next if too_big($self, $git, $blob);
- my $mime = do_cat_mail($git, $blob, \$bytes);
- my $smsg = bless {}, 'PublicInbox::Smsg';
- batch_adjust(\$max, $bytes, $batch_cb, $latest, ++$nr);
- $smsg->{blob} = $blob;
- $smsg->{bytes} = $bytes;
- $add_cb->($self, $mime, $smsg);
+ $git->cat_async($blob, \&index_both, { %$sync });
+ if ($max <= 0) {
+ $git->cat_async_wait;
+ $max = $BATCH_BYTES;
+ $batch_cb->($nr, $latest);
+ }
} elsif ($line =~ /$delmsg/o) {
my $blob = $1;
$D{$blob} = 1 unless too_big($self, $git, $blob);
$latest = $1;
$newest ||= $latest;
} elsif ($line =~ /^author .*? ([0-9]+) [\-\+][0-9]+$/) {
- $self->{autime} = $1;
+ $sync->{autime} = $1;
} elsif ($line =~ /^committer .*? ([0-9]+) [\-\+][0-9]+$/) {
- $self->{cotime} = $1;
+ $sync->{cotime} = $1;
}
}
close($log) or die "git log failed: \$?=$?";
# get the leftovers
foreach my $blob (keys %D) {
- my $mime = do_cat_mail($git, $blob, \$bytes);
- $del_cb->($self, $mime);
+ $git->cat_async($blob, \&unindex_both, $self);
}
+ $git->cat_async_wait;
$batch_cb->($nr, $latest, $newest);
}
my $xdb = $self->begin_txn_lazy;
my $mm = _msgmap_init($self);
do {
- if ($xlog) {
- close($xlog) or die "git log failed: \$?=$?";
- $xlog = undef;
- }
+ $xlog = undef; # stop previous git-log via SIGPIPE
$last_commit = _last_x_commit($self, $mm);
$lx = reindex_from($opts->{reindex}, $last_commit);
} while (_last_x_commit($self, $mm) ne $last_commit);
my $dbh = $mm->{dbh} if $mm;
- my $cb = sub {
+ my $batch_cb = sub {
my ($nr, $commit, $newest) = @_;
if ($dbh) {
if ($newest) {
};
$dbh->begin_work;
- read_log($self, $xlog, *index_both, *unindex_both, $cb);
+ read_log($self, $xlog, $batch_cb);
}
sub DESTROY {