=item --max-size=BYTES
+=item --wal
+
These options affect indexing. They have no effect if
L</--no-index> is specified
=item --batch-size SIZE
+=item --wal
+
These switches behave as they do for L<public-inbox-index(1)>
=item --all
Passed directly to L<git-log(1)> to limit changes for C<--reindex>
+=item --wal
+
+Enable WAL (Write-Ahead-Log) on SQLite files. This may reduce
+fragmentation and improve performance; but requires read-only
+processes (e.g. public-inbox-netd) to have write access
+to all directories containing *.sqlite3 files.
+See L<https://sqlite.org/wal.html> for more info.
+
=back
=head1 FILES
$self->{shards} = $self->count_shards ||
nproc_shards { nproc => $opt->{jobs} };
my $oidx = PublicInbox::OverIdx->new("$self->{xpfx}/over.sqlite3");
+ $oidx->{journal_mode} = 'wal' if $opt->{wal};
$self->{-no_fsync} = $oidx->{-no_fsync} = 1 if !$opt->{fsync};
$self->{-dangerous} = 1 if $opt->{dangerous};
$self->{oidx} = $oidx;
sub _init_v1 {
my ($self) = @_;
- my $skip_artnum = ($self->{-creat_opt} // {})->{'skip-artnum'};
- if (defined($self->{indexlevel}) || defined($skip_artnum)) {
+ my $opt = $self->{-creat_opt} // {};
+ my $skip_artnum = $opt->{'skip-artnum'};
+ if (defined($self->{indexlevel}) || defined($skip_artnum) ||
+ $opt->{wal}) {
require PublicInbox::SearchIdx;
require PublicInbox::Msgmap;
my $sidx = PublicInbox::SearchIdx->new($self, 1); # just create
+ $sidx->{oidx}->{journal_mode} = 'wal' if $opt->{wal};
$sidx->begin_txn_lazy;
- my $mm = PublicInbox::Msgmap->new_file($self, 1);
+ my $mm = PublicInbox::Msgmap->new_file($self, 1, $opt);
if (defined $skip_artnum) {
$mm->{dbh}->begin_work;
$mm->skip_artnum($skip_artnum);
my $tl = wantarray && $self->{-err_wr} ?
on_destroy(\&_tail_err, $self) :
undef;
- $eidx->idx_init({-private => 1}); # acquires lock
+ $eidx->idx_init({wal => 1, -private => 1}); # acquires lock
wantarray ? ($eidx, $tl) : $eidx;
}
use Scalar::Util qw(blessed);
sub new_file {
- my ($class, $ibx, $rw) = @_;
+ my ($class, $ibx, $rw, $opt) = @_;
my $f;
if (blessed($ibx)) {
$f = $ibx->mm_file;
return if !$rw && !-r $f;
my $self = bless { filename => $f }, $class;
+ $self->{journal_mode} = 'wal' if $opt->{wal};
my $dbh = $self->{dbh} = PublicInbox::Over::dbh_new($self, $rw);
if ($rw) {
$dbh->begin_work;
my ($dir) = ($fn =~ m!(.*?/)[^/]+\z!);
File::Path::mkpath($dir);
}
- $self->{journal_mode} = 'WAL' if $opt->{-private};
+ $self->{journal_mode} = 'wal' if $opt->{wal};
# create the DB:
PublicInbox::Over::dbh($self);
$self->dbh_close;
die "BUG: v1_mm_init is only for v1\n" if $self->{ibx}->version != 1;
$self->{mm} //= do {
require PublicInbox::Msgmap;
- PublicInbox::Msgmap->new_file($self->{ibx}, 1);
+ PublicInbox::Msgmap->new_file($self->{ibx}, 1, $self->{-opt});
};
}
local $SIG{QUIT} = $quit;
local $SIG{INT} = $quit;
local $SIG{TERM} = $quit;
+ $self->{oidx}->{journal_mode} = 'wal' if $opt->{wal};
my $xdb = $self->begin_txn_lazy;
$self->{oidx}->rethread_prepare($opt);
my $mm = v1_mm_init $self;
$opt{-primary_address} //= $addr->[0] // "$ident\@example.com";
my $parallel = delete($opt{importer_parallel}) // 0;
my $creat_opt = { nproc => delete($opt{nproc}) // 1 };
+ $creat_opt->{wal} = 1 if $opt{wal};
my $ibx = PublicInbox::InboxWritable->new({ %opt }, $creat_opt);
if (!-f "$dir/creat.stamp") {
my $im = $ibx->importer($parallel);
my $skip_epoch = ($self->{ibx}->{-creat_opt} // {})->{'skip-epoch'};
$max = $skip_epoch if (defined($skip_epoch) && !defined($max));
$self->{mg}->add_epoch($max // 0);
- $self->idx_init;
+ $self->idx_init($self->{ibx}->{-creat_opt});
my $skip_artnum = ($self->{ibx}->{-creat_opt} // {})->{'skip-artnum'};
$self->{mm}->skip_artnum($skip_artnum) if defined $skip_artnum;
$self->done;
# Now that all subprocesses are up, we can open the FDs
# for SQLite:
- my $mm = $self->{mm} = PublicInbox::Msgmap->new_file($ibx, 1);
+ my $mm = $self->{mm} = PublicInbox::Msgmap->new_file($ibx, 1, $opt);
$mm->{dbh}->begin_work;
}
# index options
qw(verbose|v+ rethread compact|c+ fsync|sync!
indexlevel|index-level|L=s max_size|max-size=s
- batch_size|batch-size=s
+ batch_size|batch-size=s wal
sequential-shard|seq-shard
)) or die $help;
if ($opt->{help}) { print $help; exit 0 };
PublicInbox::Admin::require_or_die(keys %$mods);
PublicInbox::Admin::progress_prepare($opt);
$env = PublicInbox::Admin::index_prepare($opt, $cfg);
+ if (!$opt->{wal}) {
+ $opt->{wal} = 1 if $old->over->dbh->selectrow_array(
+ 'PRAGMA journal_mode') eq 'wal';
+ $old->cleanup;
+ }
}
local %ENV = (%$env, %ENV) if $env;
my $new = { %$old };
$new->{inboxdir} = PublicInbox::Config::rel2abs_collapsed($new_dir);
$new->{version} = 2;
-$new = PublicInbox::InboxWritable->new($new, { nproc => $opt->{jobs} });
+my $creat_opt = { nproc => $opt->{jobs} };
+$creat_opt->{wal} = 1 if $opt->{wal};
+$new = PublicInbox::InboxWritable->new($new, $creat_opt);
$new->{-no_fsync} = 1 if !$opt->{fsync};
my $v2w;
EOF
my $opt = { quiet => -1, compact => 0, fsync => 1, scan => 1 };
GetOptions($opt, qw(verbose|v+ reindex rethread compact|c+ jobs|j=i
- fsync|sync! fast dangerous
+ fsync|sync! fast dangerous wal
indexlevel|index-level|L=s max_size|max-size=s
batch_size|batch-size=s
dedupe:s@ gc commit-interval=i watch scan! dry-run|n
'update-extindex' => [], # ":s@" optional arg sets '' if no arg given
};
GetOptions($opt, qw(verbose|v+ reindex rethread compact|c+ jobs|j=i prune
- fsync|sync! xapian_only|xapian-only dangerous
+ fsync|sync! xapian_only|xapian-only dangerous wal
indexlevel|index-level|L=s max_size|max-size=s
batch_size|batch-size=s
since|after=s until|before=s
my $creat_opt = {};
my %opts = (
'V|version=i' => \$version,
+ wal => \($creat_opt->{wal}),
'L|index-level|indexlevel=s' => \$indexlevel,
'S|skip|skip-epoch=i' => \($creat_opt->{'skip-epoch'}),
'skip-artnum=i' => \($creat_opt->{'skip-artnum'}),
have_xapian_compact;
my ($tmpdir, $for_destroy) = tmpdir();
my $ibx = create_inbox 'v1', indexlevel => 'medium', tmpdir => "$tmpdir/v1",
+ wal => 1,
pre_cb => sub {
my ($inboxdir) = @_;
PublicInbox::Import::init_bare($inboxdir);
'sharedRepository respected on file after convert');
}
+is $ibx->mm->{dbh}->selectrow_array('PRAGMA journal_mode'), 'wal',
+ '-compact preserves msgmap.sqlite3 wal';
+is $ibx->over->dbh->selectrow_array('PRAGMA journal_mode'), 'wal',
+ '-compact preserves over.sqlite3 wal';
+$ibx->cleanup;
+
local $ENV{PI_CONFIG} = '/dev/null';
my ($out, $err) = ('', '');
my $rdr = { 1 => \$out, 2 => \$err };
$ibx->{inboxdir} = "$tmpdir/x/v2";
$ibx->{version} = 2;
is($ibx->mm->num_highwater, $hwm, 'highwater mark unchanged in v2 inbox');
+is $ibx->mm->{dbh}->selectrow_array('PRAGMA journal_mode'), 'wal',
+ '-convert preserves msgmap.sqlite3 wal';
+is $ibx->over->dbh->selectrow_array('PRAGMA journal_mode'), 'wal',
+ '-convert preserves over.sqlite3 wal';
+$ibx->cleanup;
@xdir = glob("$tmpdir/x/v2/xap*/*");
foreach (@xdir) {
run_script([qw(-index -Lbasic), "$home/v1test"]) or BAIL_OUT "index $?";
-ok(run_script([qw(-extindex --dangerous --all), "$home/extindex"]),
+ok(run_script([qw(-extindex --dangerous --all --wal), "$home/extindex"]),
'extindex init');
{
my $es = PublicInbox::ExtSearch->new("$home/extindex");
ok($es->has_threadid, '->has_threadid');
+ my $jm = $es->over->dbh->selectrow_array('PRAGMA journal_mode');
+ is $jm, 'wal', "--wal enables `journal_mode = wal' in over.sqlite3";
}
if ('with boost') {
local $ENV{PI_CONFIG} = "$tmpdir/config";
# index master (required for v1)
- my @cmd = (qw(-index -j0 --dangerous), $ibx->{inboxdir}, "-L$level");
+ my @cmd = (qw(-index --wal -j0 --dangerous),
+ $ibx->{inboxdir}, "-L$level");
push @cmd, '-c' if have_xapian_compact;
ok(run_script(\@cmd, undef, { 2 => \$err }), 'index master');
my $ro_master = PublicInbox::Inbox->new({
my $msgs = $ro_master->over->recent;
is(scalar(@$msgs), 1, 'only one message in master, so far');
is($msgs->[0]->{mid}, 'm@1', 'first message in master indexed');
+ my $jm = $ro_master->over->dbh->selectrow_array('PRAGMA journal_mode');
+ is $jm, 'wal', 'over.sqlite3 respects --wal';
+ $jm = $ro_master->mm->{dbh}->selectrow_array('PRAGMA journal_mode');
+ is $jm, 'wal', 'msgmap.sqlite3 respects --wal';
# clone
@cmd = (qw(git clone --mirror -q));
ok(!run_script($cmd, undef, { 2 => \$err, 1 => \$err }), $msg);
}
+my $check_wal = sub {
+ my ($bn) = ($_[0] =~ m!/([^/]+)/*\z!);
+ my $ibx = PublicInbox::Inbox->new({inboxdir => $_[0]});
+ my $jm = $ibx->over->dbh->selectrow_array('PRAGMA journal_mode');
+ is $jm, 'wal', "--wal works with $bn (over.sqlite3)";
+ $jm = $ibx->mm->{dbh}->selectrow_array('PRAGMA journal_mode');
+ is $jm, 'wal', "--wal works with $bn (msgmap.sqlite3)";
+};
+
{
local $ENV{PI_DIR} = "$tmpdir/.public-inbox/";
my $cfgfile = "$ENV{PI_DIR}/config";
- my $cmd = [ '-init', 'blist', "$tmpdir/blist",
+ my $cmd = [ qw(-init blist), "$tmpdir/blist",
qw(http://example.com/blist blist@example.com) ];
my $umask = umask(070) // xbail "umask: $!";
ok(run_script($cmd), 'public-inbox-init OK');
local $ENV{PI_DIR} = "$tmpdir/.public-inbox/";
local $ENV{PI_EMERGENCY} = "$tmpdir/.public-inbox/emergency";
my $cfgfile = "$ENV{PI_DIR}/config";
- my $cmd = [ '-init', '-V2', 'v2list', "$tmpdir/v2list",
+ my $cmd = [ qw(-init -V2 --wal v2list), "$tmpdir/v2list",
qw(http://example.com/v2list v2list@example.com) ];
ok(run_script($cmd), 'public-inbox-init -V2 OK');
ok(-d "$tmpdir/v2list", 'v2list directory exists');
ok(-f "$tmpdir/v2list/msgmap.sqlite3", 'msgmap exists');
ok(-d "$tmpdir/v2list/all.git", 'catch-all.git directory exists');
+ $check_wal->("$tmpdir/v2list");
$cmd = [ '-init', 'v2list', "$tmpdir/v2list",
qw(http://example.com/v2list v2list@example.com) ];
ok(run_script($cmd), 'public-inbox-init is idempotent');
$addr = 'skip4@example.com';
$env = { ORIGINAL_RECIPIENT => $addr };
- $cmd = [ qw(-init -V1 --skip-artnum 12 -Lmedium skip4), "$tmpdir/skip4",
- qw(http://example.com/skip4), $addr ];
+ $cmd = [ qw(-init -V1 --wal --skip-artnum 12 -Lmedium skip4),
+ "$tmpdir/skip4", qw(http://example.com/skip4), $addr ];
ok(run_script($cmd), '--skip-artnum -V1');
$err = '';
ok(run_script([qw(-mda --no-precheck)], $env, $rdr), 'deliver V1');
"$tmpdir/skip4/public-inbox/msgmap.sqlite3");
$n = $mm->num_for($mid);
is($n, 13, 'V1 NNTP article numbers skipped via --skip-artnum');
+ $check_wal->("$tmpdir/skip4");
+
+ # n.b. for now, indexlevel defaults to `full' if unspecified w/
+ # --wal or --skip-artnum
+ $cmd = [ qw(-init -V1 --wal wal-only-v1), "$tmpdir/wal-1",
+ qw(http://example.com/wal-1 wal@example.com) ];
+ ok run_script($cmd), '--wal with -V1';
+ $check_wal->("$tmpdir/wal-1");
}
{