sub add_xapian ($$$$) {
        my ($self, $eml, $smsg, $mids) = @_;
+       begin_txn_lazy($self);
        my $doc = eml2doc($self, $eml, $smsg, $mids);
        $self->{xdb}->replace_document($smsg->{num}, $doc);
 }
 
 sub index_eml {
        my ($self, $eml, $smsg, $eidx_key) = @_;
        $smsg->{eidx_key} = $eidx_key if defined $eidx_key;
-       $self->ipc_do('add_message', $eml, $smsg);
+       $self->ipc_do('add_xapian', $eml, $smsg);
 }
 
 # wait for return to determine when ipc_do('commit_txn_lazy') is done
 
 sub do_idx ($$$) {
        my ($self, $eml, $smsg) = @_;
        $self->{oidx}->add_overview($eml, $smsg);
-       my $idx = idx_shard($self, $smsg->{num});
-       $idx->index_eml($eml, $smsg);
+       if ($self->{-need_xapian}) {
+               my $idx = idx_shard($self, $smsg->{num});
+               $idx->index_eml($eml, $smsg);
+       }
        my $n = $self->{transact_bytes} += $smsg->{bytes};
        $n >= $self->{batch_bytes};
 }
        my $max = $self->{shards} - 1;
        my $idx = $self->{idx_shards} = [];
        push @$idx, PublicInbox::SearchIdxShard->new($self, $_) for (0..$max);
+       $self->{-need_xapian} = $idx->[0]->need_xapian;
 
        # SearchIdxShard may do their own flushing, so don't scale
        # until after forking
 sub unindex_oid_aux ($$$) {
        my ($self, $oid, $mid) = @_;
        my @removed = $self->{oidx}->remove_oid($oid, $mid);
+       return unless $self->{-need_xapian};
        for my $num (@removed) {
                idx_shard($self, $num)->ipc_do('xdb_remove', $num);
        }