mirror of
https://github.com/beetbox/beets.git
synced 2026-01-05 23:43:31 +01:00
exclusive database access (GC-399)
This is the crux of the matter. This new lock synchronizes accesses to the database itself, allowing only one thread to have transactions active at a time. In effect, this makes sure that beets only has one SQLite transaction active at a time (and is very conservative in this effort; no read sharing is allowed either). This prevents all contention in SQLite and hides the pathological HAVE_SLEEP=0 behavior.
This commit is contained in:
parent
5d6e9b387a
commit
b487001c9b
1 changed files with 16 additions and 0 deletions
|
|
@ -912,7 +912,12 @@ class Transaction(object):
|
|||
another is active in a different thread.
|
||||
"""
|
||||
with self.lib._tx_stack() as stack:
|
||||
first = not stack
|
||||
stack.append(self)
|
||||
if first:
|
||||
# Beginning a "root" transaction, which corresponds to an
|
||||
# SQLite transaction.
|
||||
self.lib._db_lock.acquire()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
|
|
@ -924,7 +929,9 @@ class Transaction(object):
|
|||
assert stack.pop() is self
|
||||
empty = not stack
|
||||
if empty:
|
||||
# Ending a "root" transaction. End the SQLite transaction.
|
||||
self.lib._connection().commit()
|
||||
self.lib._db_lock.release()
|
||||
|
||||
def query(self, statement, subvals=()):
|
||||
"""Execute an SQL statement with substitution values and return
|
||||
|
|
@ -972,6 +979,15 @@ class Library(BaseLibrary):
|
|||
# A lock to protect the _connections and _tx_stacks maps, which
|
||||
# both map thread IDs to private resources.
|
||||
self._shared_map_lock = threading.Lock()
|
||||
# A lock to protect access to the database itself. SQLite does
|
||||
# allow multiple threads to access the database at the same
|
||||
# time, but many users were experiencing crashes related to this
|
||||
# capability: where SQLite was compiled without HAVE_USLEEP, its
|
||||
# backoff algorithm in the case of contention was causing
|
||||
# whole-second sleeps (!) that would trigger its internal
|
||||
# timeout. Using this lock ensures only one SQLite transaction
|
||||
# is active at a time.
|
||||
self._db_lock = threading.Lock()
|
||||
|
||||
# Set up database schema.
|
||||
self._make_table('items', item_fields)
|
||||
|
|
|
|||
Loading…
Reference in a new issue