[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 3/4] mlib: use flock to get around SQLite busy errors
From: |
Eric Wong |
Subject: |
[PATCH 3/4] mlib: use flock to get around SQLite busy errors |
Date: |
Tue, 30 Jan 2018 09:17:11 +0000 |
The SQLite busy waiting scheme isn't great for usability
and the busy timeout is done by sleep+backoff. I prefer
to say we only support filesystems with flock() for our
little DB.
---
lib/dtas/mlib.rb | 33 ++++++++++++++++++++++++++-------
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/lib/dtas/mlib.rb b/lib/dtas/mlib.rb
index d1707fb..3f5763f 100644
--- a/lib/dtas/mlib.rb
+++ b/lib/dtas/mlib.rb
@@ -10,6 +10,7 @@
require_relative 'source/ff'
require_relative 'source/splitfx'
require 'socket'
+require 'tempfile'
# For the DTAS Music Library, based on what MPD uses.
class DTAS::Mlib # :nodoc:
@@ -62,15 +63,24 @@ def initialize(db)
]
end
+ def synchronize
+ @lock.flock(File::LOCK_EX)
+ @db.transaction { yield }
+ ensure
+ @lock.flock(File::LOCK_UN)
+ end
+
def init_suffixes
`sox --help 2>/dev/null` =~ /\nAUDIO FILE FORMATS:\s*([^\n]+)/
re = $1.split(/\s+/).map! { |x| Regexp.quote(x) }.join('|')
@suffixes = Regexp.new("\\.(?:#{re}|yml)\\z", Regexp::IGNORECASE)
end
- def worker(todo)
+ def worker(todo, lock)
+ old_lock = @lock
+ @lock = lock
@work.close
- @db.tables # reconnect before chdir
+ synchronize { @db.tables } # reconnect before chdir
@pwd = Dir.pwd.b
begin
buf = todo.recv(16384) # 4x bigger than PATH_MAX ought to be enough
@@ -84,7 +94,7 @@ def worker(todo)
end
def ignore(job)
- @db.transaction do
+ synchronize do
node_ensure(job.parent_id, job.path, DM_IGN, job.ctime)
end
end
@@ -112,7 +122,7 @@ def worker_work(job)
tmp[tag_id] = value if value.valid_encoding?
end
end
- @db.transaction do
+ synchronize do
node_id = node_ensure(job.parent_id, path, tlen, job.ctime)[:id]
vals = @db[:vals]
comments = @db[:comments]
@@ -143,11 +153,20 @@ def update(path, opts = nil)
@work and raise 'update already running'
todo, @work = UNIXSocket.pair(:SOCK_SEQPACKET)
@db.disconnect
- jobs.times { |i| fork { worker(todo) } }
+
+ # like a Mutex between processes
+ @lock = Tempfile.new('dtas.mlib.lock')
+ jobs.times do |i|
+ lock = File.open(@lock.path, 'w')
+ fork { worker(todo, lock) }
+ lock.close
+ end
+ @lock.unlink
todo.close
scan_dir(path, st)
@work.close
Process.waitall
+ @lock.close
ensure
@work = nil
end
@@ -235,7 +254,7 @@ def root_node
end
def dir_vivify(parts, ctime)
- @db.transaction do
+ synchronize do
dir = root_node
last = parts.pop
parts.each do |name|
@@ -289,7 +308,7 @@ def scan_dir(path, st, parent_id = nil)
dir = dir_vivify(@pwd.split(%r{/+}n), st.ctime.to_i)
dir_id = dir[:id]
- @db.transaction do
+ synchronize do
@db[:nodes].where(parent_id: dir_id).each do |node|
File.exist?(node[:name]) or remove_entry(node)
end
--
EW