server/sources/extlite.py
branchstable
changeset 2053 fb156d69bfd9
parent 1977 606923dff11b
child 2062 20f18837107c
equal deleted inserted replaced
2052:0b9b0bdc93f5 2053:fb156d69bfd9
     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