Edgewall Software

Ticket #2170: trac_no_writer_contention.patch

File trac_no_writer_contention.patch, 4.1 KB (added by cmlenz, 3 years ago)

Minor cleanup to the previous patch

  • trac/db.py

     
    2424except ImportError: 
    2525    import dummy_threading as threading 
    2626    threading._get_ident = lambda: 0 
     27import weakref 
    2728 
    2829from trac.core import TracError 
    2930 
     
    183184                    del self._active[tid] 
    184185                    if cnx not in self._dormant: 
    185186                        cnx.rollback() 
    186                         self._dormant.append(cnx) 
     187                        if cnx.poolable: 
     188                            self._dormant.append(cnx) 
     189                        else: 
     190                            self._cursize -= 1 
    187191                        self._available.notify() 
    188192        finally: 
    189193            self._available.release() 
     
    202206    have_pysqlite = 2 
    203207 
    204208    class PyFormatCursor(sqlite.Cursor): 
     209        def _rollback_on_error(self, function, *args, **kwargs): 
     210            try: 
     211                return function(self, *args, **kwargs) 
     212            except sqlite.OperationalError, e: 
     213                self.cnx.rollback() 
     214                raise 
    205215        def execute(self, sql, args=None): 
    206216            if args: 
    207217                sql = sql % (('?',) * len(args)) 
    208             sqlite.Cursor.execute(self, sql, args or []) 
     218            return self._rollback_on_error(sqlite.Cursor.execute, sql, 
     219                                           args or []) 
    209220        def executemany(self, sql, args=None): 
    210221            if args: 
    211222                sql = sql % (('?',) * len(args[0])) 
    212             sqlite.Cursor.executemany(self, sql, args or []) 
     223            return self._rollback_on_error(sqlite.Cursor.executemany, sql, 
     224                                           args or []) 
    213225        def _convert_row(self, row): 
    214226            return tuple([(isinstance(v, unicode) and [v.encode('utf-8')] or [v])[0] 
    215227                          for v in row]) 
     
    224236            rows = sqlite.Cursor.fetchall(self) 
    225237            return rows != None and [self._convert_row(row) 
    226238                                     for row in rows] or None 
    227                  
     239 
    228240except ImportError: 
    229241    try: 
    230242        import sqlite 
     
    236248class SQLiteConnection(ConnectionWrapper): 
    237249    """Connection wrapper for SQLite.""" 
    238250 
    239     __slots__ = ['cnx'] 
     251    __slots__ = ['cnx', '_active_cursors'] 
    240252 
     253    poolable = False 
     254 
    241255    def __init__(self, path, params={}): 
    242256        assert have_pysqlite > 0 
    243257        self.cnx = None 
     
    254268                                 'directory it is located in.' \ 
    255269                                 % (getuser(), path) 
    256270 
    257         timeout = int(params.get('timeout', 10000)) 
    258271        if have_pysqlite == 2: 
     272            self._active_cursors = weakref.WeakKeyDictionary() 
     273            timeout = int(params.get('timeout', 10.0)) 
    259274            # Convert unicode to UTF-8 bytestrings. This is case-sensitive, so 
    260275            # we need two converters 
    261276            sqlite.register_converter('text', str) 
    262277            sqlite.register_converter('TEXT', str) 
    263278 
    264279            cnx = sqlite.connect(path, detect_types=sqlite.PARSE_DECLTYPES, 
    265                                  check_same_thread=False, timeout=timeout) 
     280                                 timeout=timeout) 
    266281        else: 
     282            timeout = int(params.get('timeout', 10000)) 
    267283            cnx = sqlite.connect(path, timeout=timeout) 
    268284        ConnectionWrapper.__init__(self, cnx) 
    269285 
    270286    if have_pysqlite == 2: 
    271287        def cursor(self): 
    272             return self.cnx.cursor(PyFormatCursor) 
     288            cursor = self.cnx.cursor(PyFormatCursor) 
     289            self._active_cursors[cursor] = True 
     290            cursor.cnx = self 
     291            return cursor 
     292 
     293        def rollback(self): 
     294            for cursor in self._active_cursors.keys(): 
     295                cursor.close() 
     296            self.cnx.rollback() 
     297 
    273298    else: 
    274299        def cursor(self): 
    275300            return self.cnx.cursor() 
     
    332357 
    333358    __slots__ = ['cnx'] 
    334359 
     360    poolable = True 
     361 
    335362    def __init__(self, path, user=None, password=None, host=None, port=None, 
    336363                 params={}): 
    337364        if path.startswith('/'):