diff options
| author | Victor Shyba <victor1984@riseup.net> | 2017-09-11 21:24:50 -0300 | 
|---|---|---|
| committer | Victor Shyba <victor1984@riseup.net> | 2017-09-11 21:24:50 -0300 | 
| commit | 969ab4ebeda2ac3abcb00d9beded5f013e074e4a (patch) | |
| tree | 0363ee9339582ada637ae040f18b5b663b8ebc49 | |
| parent | ae11ec7d685cdf313778e0c5126ecd748608be26 (diff) | |
[bug] do not allow concurrent schema creation
Moved schema creation and migrations to the pragma locked call, so we
avoid it running concurrently on a thread pool.
-- Resolves: #8945
| -rw-r--r-- | src/leap/soledad/client/_db/blobs.py | 25 | ||||
| -rw-r--r-- | src/leap/soledad/client/_db/pragmas.py | 4 | 
2 files changed, 19 insertions, 10 deletions
diff --git a/src/leap/soledad/client/_db/blobs.py b/src/leap/soledad/client/_db/blobs.py index cfabda74..1447e3bb 100644 --- a/src/leap/soledad/client/_db/blobs.py +++ b/src/leap/soledad/client/_db/blobs.py @@ -73,6 +73,20 @@ class SyncStatus:  class ConnectionPool(adbapi.ConnectionPool): +    def blocking_create_schema(self, create_schema_function): +        """ +        Grabs a connection and executes schema creation from current thread, +        disconnecting right after it. This ensures that schema creation blocks +        everything and doesn't run concurrently. + +        :param create_schema_function: A function that receives a connection +            and executes schema criation and/or migrations. +        :type create_schema_function: function +        """ +        conn = self.connect() +        create_schema_function(conn) +        self.disconnect(conn) +      def insertAndGetLastRowid(self, *args, **kwargs):          """          Execute an SQL query and return the last rowid. @@ -439,8 +453,8 @@ class SQLiteBlobBackend(object):          opts = sqlcipher.SQLCipherOptions(              '/tmp/ignored', binascii.b2a_hex(key),              is_raw_key=True, create=True) -        pragmafun = partial(pragmas.set_init_pragmas, opts=opts) -        openfun = _sqlcipherInitFactory(pragmafun) +        openfun = partial(pragmas.set_init_pragmas, opts=opts, +                          schema_func=_init_blob_table)          self.dbpool = ConnectionPool(              backend, self.path, check_same_thread=False, timeout=5, @@ -553,13 +567,6 @@ def _init_blob_table(conn):          conn.execute('ALTER TABLE blobs ADD COLUMN retries INT default 0') -def _sqlcipherInitFactory(fun): -    def _initialize(conn): -        fun(conn) -        _init_blob_table(conn) -    return _initialize - -  #  # testing facilities  # diff --git a/src/leap/soledad/client/_db/pragmas.py b/src/leap/soledad/client/_db/pragmas.py index 870ed63e..76fea458 100644 --- a/src/leap/soledad/client/_db/pragmas.py +++ b/src/leap/soledad/client/_db/pragmas.py @@ -31,7 +31,7 @@ logger = getLogger(__name__)  _db_init_lock = threading.Lock() -def set_init_pragmas(conn, opts=None, extra_queries=None): +def set_init_pragmas(conn, opts=None, extra_queries=None, schema_func=None):      """      Set the initialization pragmas. @@ -43,6 +43,8 @@ def set_init_pragmas(conn, opts=None, extra_queries=None):      with _db_init_lock:          # only one execution path should initialize the db          _set_init_pragmas(conn, opts, extra_queries) +        if schema_func: +            schema_func(conn)  def _set_init_pragmas(conn, opts, extra_queries):  | 
