6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
7 """ |
7 """ |
8 __docformat__ = "restructuredtext en" |
8 __docformat__ = "restructuredtext en" |
9 |
9 |
10 |
10 |
11 import time |
|
12 import threading |
|
13 from os.path import join, exists |
11 from os.path import join, exists |
14 |
12 |
15 from cubicweb import server |
13 from cubicweb import server |
16 from cubicweb.server.sqlutils import SQL_PREFIX, sqlexec, SQLAdapterMixIn |
14 from cubicweb.server.sqlutils import SQL_PREFIX, sqlexec, SQLAdapterMixIn |
17 from cubicweb.server.sources import AbstractSource, native |
15 from cubicweb.server.sources import AbstractSource, native |
18 from cubicweb.server.sources.rql2sql import SQLGenerator |
16 from cubicweb.server.sources.rql2sql import SQLGenerator |
19 |
17 |
20 def timeout_acquire(lock, timeout): |
|
21 while not lock.acquire(False): |
|
22 time.sleep(0.2) |
|
23 timeout -= 0.2 |
|
24 if timeout <= 0: |
|
25 raise RuntimeError("svn source is busy, can't acquire connection lock") |
|
26 |
|
27 class ConnectionWrapper(object): |
18 class ConnectionWrapper(object): |
28 def __init__(self, source=None): |
19 def __init__(self, source=None): |
29 self.source = source |
20 self.source = source |
30 self._cnx = None |
21 self._cnx = None |
31 |
22 |
32 @property |
|
33 def cnx(self): |
|
34 if self._cnx is None: |
|
35 timeout_acquire(self.source._cnxlock, 5) |
|
36 self._cnx = self.source._sqlcnx |
|
37 return self._cnx |
|
38 |
|
39 def commit(self): |
23 def commit(self): |
40 if self._cnx is not None: |
24 if self._cnx is not None: |
41 self._cnx.commit() |
25 self._cnx.commit() |
42 |
26 |
43 def rollback(self): |
27 def rollback(self): |
44 if self._cnx is not None: |
28 if self._cnx is not None: |
45 self._cnx.rollback() |
29 self._cnx.rollback() |
46 |
30 |
47 def cursor(self): |
31 def cursor(self): |
48 return self.cnx.cursor() |
32 if self._cnx is None: |
|
33 self._cnx = self.source._sqlcnx |
|
34 return self._cnx.cursor() |
49 |
35 |
50 |
36 |
51 class SQLiteAbstractSource(AbstractSource): |
37 class SQLiteAbstractSource(AbstractSource): |
52 """an abstract class for external sources using a sqlite database helper |
38 """an abstract class for external sources using a sqlite database helper |
53 """ |
39 """ |
85 # which will call set_schema |
71 # which will call set_schema |
86 self._need_sql_create = not exists(dbpath) |
72 self._need_sql_create = not exists(dbpath) |
87 self._need_full_import = self._need_sql_create |
73 self._need_full_import = self._need_sql_create |
88 AbstractSource.__init__(self, repo, appschema, source_config, |
74 AbstractSource.__init__(self, repo, appschema, source_config, |
89 *args, **kwargs) |
75 *args, **kwargs) |
90 # sql database can only be accessed by one connection at a time, and a |
|
91 # connection can only be used by the thread which created it so: |
|
92 # * create the connection when needed |
|
93 # * use a lock to be sure only one connection is used |
|
94 self._cnxlock = threading.Lock() |
|
95 |
76 |
96 @property |
77 @property |
97 def _sqlcnx(self): |
78 def _sqlcnx(self): |
98 # XXX: sqlite connections can only be used in the same thread, so |
79 # XXX: sqlite connections can only be used in the same thread, so |
99 # create a new one each time necessary. If it appears to be time |
80 # create a new one each time necessary. If it appears to be time |
162 """the pool using the given connection is being reseted from its current |
143 """the pool using the given connection is being reseted from its current |
163 attached session: release the connection lock if the connection wrapper |
144 attached session: release the connection lock if the connection wrapper |
164 has a connection set |
145 has a connection set |
165 """ |
146 """ |
166 if cnx._cnx is not None: |
147 if cnx._cnx is not None: |
167 try: |
148 cnx._cnx.close() |
168 cnx._cnx.close() |
149 # reset _cnx to ensure next thread using cnx will get a new |
169 cnx._cnx = None |
150 # connection |
170 finally: |
151 cnx._cnx = None |
171 self._cnxlock.release() |
|
172 |
152 |
173 def syntax_tree_search(self, session, union, |
153 def syntax_tree_search(self, session, union, |
174 args=None, cachekey=None, varmap=None, debug=0): |
154 args=None, cachekey=None, varmap=None, debug=0): |
175 """return result from this source for a rql query (actually from a rql |
155 """return result from this source for a rql query (actually from a rql |
176 syntax tree and a solution dictionary mapping each used variable to a |
156 syntax tree and a solution dictionary mapping each used variable to a |