change extlite connection handling: connection may not be shared among threads but it's fine to have multiple connections open in several threads (as demonstrated by unittest_extlite)
--- a/server/sources/extlite.py Fri Jun 05 10:40:54 2009 +0200
+++ b/server/sources/extlite.py Fri Jun 05 13:21:31 2009 +0200
@@ -8,8 +8,6 @@
__docformat__ = "restructuredtext en"
-import time
-import threading
from os.path import join, exists
from cubicweb import server
@@ -17,25 +15,11 @@
from cubicweb.server.sources import AbstractSource, native
from cubicweb.server.sources.rql2sql import SQLGenerator
-def timeout_acquire(lock, timeout):
- while not lock.acquire(False):
- time.sleep(0.2)
- timeout -= 0.2
- if timeout <= 0:
- raise RuntimeError("svn source is busy, can't acquire connection lock")
-
class ConnectionWrapper(object):
def __init__(self, source=None):
self.source = source
self._cnx = None
- @property
- def cnx(self):
- if self._cnx is None:
- timeout_acquire(self.source._cnxlock, 5)
- self._cnx = self.source._sqlcnx
- return self._cnx
-
def commit(self):
if self._cnx is not None:
self._cnx.commit()
@@ -45,7 +29,9 @@
self._cnx.rollback()
def cursor(self):
- return self.cnx.cursor()
+ if self._cnx is None:
+ self._cnx = self.source._sqlcnx
+ return self._cnx.cursor()
class SQLiteAbstractSource(AbstractSource):
@@ -87,11 +73,6 @@
self._need_full_import = self._need_sql_create
AbstractSource.__init__(self, repo, appschema, source_config,
*args, **kwargs)
- # sql database can only be accessed by one connection at a time, and a
- # connection can only be used by the thread which created it so:
- # * create the connection when needed
- # * use a lock to be sure only one connection is used
- self._cnxlock = threading.Lock()
@property
def _sqlcnx(self):
@@ -164,11 +145,10 @@
has a connection set
"""
if cnx._cnx is not None:
- try:
- cnx._cnx.close()
- cnx._cnx = None
- finally:
- self._cnxlock.release()
+ cnx._cnx.close()
+ # reset _cnx to ensure next thread using cnx will get a new
+ # connection
+ cnx._cnx = None
def syntax_tree_search(self, session, union,
args=None, cachekey=None, varmap=None, debug=0):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/unittest_extlite.py Fri Jun 05 13:21:31 2009 +0200
@@ -0,0 +1,62 @@
+import threading, os, time
+
+from logilab.common.testlib import TestCase, unittest_main
+from logilab.common.db import get_connection
+
+class SQLiteTC(TestCase):
+ sqlite_file = '_extlite_test.sqlite'
+ def setUp(self):
+ cnx1 = get_connection('sqlite', database=self.sqlite_file)
+ print 'SET IP'
+ cu = cnx1.cursor()
+ cu.execute('CREATE TABLE toto(name integer);')
+ cnx1.commit()
+ cnx1.close()
+
+ def tearDown(self):
+ try:
+ os.remove(self.sqlite_file)
+ except:
+ pass
+ def test(self):
+ lock = threading.Lock()
+
+ def run_thread():
+ print 'run_thread'
+ cnx2 = get_connection('sqlite', database=self.sqlite_file)
+ lock.acquire()
+ print 't2 sel1'
+ cu = cnx2.cursor()
+ cu.execute('SELECT name FROM toto')
+ self.failIf(cu.fetchall())
+ cnx2.commit()
+ print 'done'
+ lock.release()
+ time.sleep(0.1)
+ lock.acquire()
+ print 't2 sel2'
+ cu.execute('SELECT name FROM toto')
+ self.failUnless(cu.fetchall())
+ print 'done'
+ lock.release()
+
+ cnx1 = get_connection('sqlite', database=self.sqlite_file)
+ lock.acquire()
+ thread = threading.Thread(target=run_thread)
+ thread.start()
+ cu = cnx1.cursor()
+ print 't1 sel'
+ cu.execute('SELECT name FROM toto')
+ print 'done'
+ lock.release()
+ time.sleep(0.1)
+ cnx1.commit()
+ lock.acquire()
+ print 't1 insert'
+ cu.execute("INSERT INTO toto(name) VALUES ('toto')")
+ cnx1.commit()
+ print 'done'
+ lock.release()
+
+if __name__ == '__main__':
+ unittest_main()