diff options
author | Eric Wong <e@yhbt.net> | 2020-07-14 02:14:31 +0000 |
---|---|---|
committer | Eric Wong <e@yhbt.net> | 2020-07-14 23:22:59 +0000 |
commit | 322a79a6421b5993775f63ea25294a97c5203ac0 (patch) | |
tree | 32e94eb9dafead35e830fee28cb90d3bc43ec7c4 /lib/PublicInbox/Msgmap.pm | |
parent | e9fc1290ead44e06d20ff58e0a6acb5306d4fbe2 (diff) | |
download | public-inbox-322a79a6421b5993775f63ea25294a97c5203ac0.tar.gz |
While it's even less common to experience a replaced msgmap.sqlite3 file, BOFHs may do the darndest things. This is another step towards reducing the number of needless wakeups we need to do in long-lived read-only daemons.
Diffstat (limited to 'lib/PublicInbox/Msgmap.pm')
-rw-r--r-- | lib/PublicInbox/Msgmap.pm | 59 |
1 files changed, 31 insertions, 28 deletions
diff --git a/lib/PublicInbox/Msgmap.pm b/lib/PublicInbox/Msgmap.pm index aa07e344..e86fb854 100644 --- a/lib/PublicInbox/Msgmap.pm +++ b/lib/PublicInbox/Msgmap.pm @@ -13,6 +13,7 @@ use warnings; use DBI; use DBD::SQLite; use File::Temp qw(tempfile); +use PublicInbox::Over; sub new { my ($class, $git_dir, $writable) = @_; @@ -24,29 +25,13 @@ sub new { new_file($class, "$d/msgmap.sqlite3", $writable); } -sub dbh_new { - my ($f, $writable) = @_; - if ($writable && !-f $f) { # SQLite defaults mode to 0644, we want 0666 - open my $fh, '+>>', $f or die "failed to open $f: $!"; - } - my $dbh = DBI->connect("dbi:SQLite:dbname=$f",'','', { - AutoCommit => 1, - RaiseError => 1, - PrintError => 0, - ReadOnly => !$writable, - sqlite_use_immediate_transaction => 1, - }); - $dbh; -} - sub new_file { - my ($class, $f, $writable) = @_; - return if !$writable && !-r $f; + my ($class, $f, $rw) = @_; + return if !$rw && !-r $f; - my $dbh = dbh_new($f, $writable); - my $self = bless { dbh => $dbh }, $class; - - if ($writable) { + my $self = bless { filename => $f }, $class; + my $dbh = $self->{dbh} = PublicInbox::Over::dbh_new($self, $rw); + if ($rw) { create_tables($dbh); # TRUNCATE reduces I/O compared to the default (DELETE) @@ -70,7 +55,6 @@ sub tmp_clone { my $tmp = ref($self)->new_file($fn, 1); $tmp->{dbh}->do('PRAGMA synchronous = OFF'); $tmp->{dbh}->do('PRAGMA journal_mode = MEMORY'); - $tmp->{tmp_name} = $fn; # SQLite won't work if unlinked, apparently $tmp->{pid} = $$; close $fh or die "failed to close $fn: $!"; $tmp; @@ -246,28 +230,28 @@ sub mid_set { sub DESTROY { my ($self) = @_; delete $self->{dbh}; - my $f = delete $self->{tmp_name}; - if (defined $f && $self->{pid} == $$) { + my $f = $self->{filename}; + if (($self->{pid} // 0) == $$) { unlink $f or warn "failed to unlink $f: $!\n"; } } sub atfork_parent { my ($self) = @_; - my $f = $self->{tmp_name} or die "not a temporary clone\n"; + $self->{pid} or die "not a temporary clone\n"; delete $self->{dbh} and die "tmp_clone dbh not prepared for parent"; - my $dbh = $self->{dbh} = dbh_new($f, 1); + my $dbh = $self->{dbh} = PublicInbox::Over::dbh_new($self, 1); $dbh->do('PRAGMA synchronous = OFF'); } sub atfork_prepare { my ($self) = @_; - my $f = $self->{tmp_name} or die "not a temporary clone\n"; + $self->{pid} or die "not a temporary clone\n"; $self->{pid} == $$ or die "BUG: atfork_prepare not called from $self->{pid}\n"; $self->{dbh} or die "temporary clone not open\n"; # must clobber prepared statements - %$self = (tmp_name => $f, pid => $$); + %$self = (filename => $self->{filename}, pid => $$); } sub skip_artnum { @@ -296,4 +280,23 @@ sub skip_artnum { } } +sub check_inodes { + my ($self) = @_; + # no filename if in-:memory: + my $f = $self->{dbh}->sqlite_db_filename // return; + if (my @st = stat($f)) { # did st_dev, st_ino change? + my $st = pack('dd', $st[0], $st[1]); + if ($st ne ($self->{st} // $st)) { + my $tmp = eval { ref($self)->new_file($f) }; + if ($@) { + warn "E: DBI->connect($f): $@\n"; + } else { + %$self = %$tmp; + } + } + } else { + warn "W: stat $f: $!\n"; + } +} + 1; |