Edgewall Software

Ticket #3504: db_pool_timeout-r3868.patch

File db_pool_timeout-r3868.patch, 6.2 KB (added by cboos, 6 years ago)

There was apparently a missing decrement of the cursize, potentially the cause for a hang. We'll see…

  • trac/db/api.py

     
    1717import os 
    1818import urllib 
    1919 
    20 from trac.config import Option 
     20from trac.config import Option, FloatOption 
    2121from trac.core import * 
    2222from trac.db.pool import ConnectionPool 
    2323 
     
    5757        [wiki:TracEnvironment#DatabaseConnectionStrings string] for this 
    5858        project""") 
    5959 
     60    timeout = FloatOption('trac', 'timeout', '20.0', 
     61        """Timeout value for database connection, in seconds. 
     62        Use '0' to specify ''no timeout''. ''(Since 0.11)''""") 
     63 
    6064    def __init__(self): 
    6165        self._cnx_pool = None 
    6266 
     
    6872        if not self._cnx_pool: 
    6973            connector, args = self._get_connector() 
    7074            self._cnx_pool = ConnectionPool(5, connector, **args) 
    71         return self._cnx_pool.get_cnx() 
     75        return self._cnx_pool.get_cnx(self.timeout or None) 
    7276 
    7377    def shutdown(self, tid=None): 
    7478        if self._cnx_pool: 
  • trac/db/pool.py

     
    7979                        break 
    8080                    except Exception: 
    8181                        cnx.close() 
     82                        self._cursize -= 1 
    8283                elif self._maxsize and self._cursize < self._maxsize: 
    8384                    cnx = self._connector.get_connection(**self._kwargs) 
    8485                    self._cursize += 1 
    8586                    break 
    8687                else: 
    8788                    if timeout: 
     89                        if (time.time() - start) >= timeout: 
     90                            raise TimeoutError('Unable to get database ' 
     91                                               'connection within %d seconds' 
     92                                                % timeout) 
    8893                        self._available.wait(timeout) 
    89                         if (time.time() - start) >= timeout: 
    90                             raise TimeoutError, 'Unable to get database ' \ 
    91                                                 'connection within %d seconds' \ 
    92                                                 % timeout 
    93                     else: 
     94                    else: # Warning: without timeout, Trac *might* hang 
    9495                        self._available.wait() 
    9596            self._active[tid] = [1, cnx] 
    9697            return PooledConnection(self, cnx, tid) 
     
    116117        # Note: self._available *must* be acquired 
    117118        if tid in self._active: 
    118119            cnx = self._active.pop(tid)[1] 
    119             if cnx not in self._dormant: # hm, how could that happen? 
    120                 if cnx.poolable: # i.e. we can manipulate it from other threads 
    121                     cnx.rollback() 
    122                     self._dormant.append(cnx) 
    123                 elif tid == threading._get_ident(): 
    124                     cnx.rollback() # non-poolable but same thread: close 
    125                     cnx.close() 
    126                     self._cursize -= 1 
    127                 else: # non-poolable, different thread: push it back 
    128                     self._active[tid] = [0, cnx] 
    129                 self._available.notify() 
     120            assert cnx not in self._dormant 
     121            if cnx.poolable: # i.e. we can manipulate it from other threads 
     122                cnx.rollback() 
     123                self._dormant.append(cnx) 
     124            elif tid == threading._get_ident(): 
     125                cnx.rollback() # non-poolable but same thread: close 
     126                cnx.close() 
     127                self._cursize -= 1 
     128            else: # non-poolable, different thread: push it back 
     129                self._active[tid] = [0, cnx] 
     130                return # no need to notify other threads 
     131            self._available.notify() 
    130132 
    131133    def shutdown(self, tid=None): 
    132134        self._available.acquire() 
  • trac/config.py

     
    2525from trac.core import ExtensionPoint, TracError 
    2626from trac.util import sorted, to_unicode 
    2727 
    28 __all__ = ['Configuration', 'Option', 'BoolOption', 'IntOption', 'ListOption', 
     28__all__ = ['Configuration', 'Option', 
     29           'BoolOption', 'IntOption', 'FloatOption', 'ListOption', 
    2930           'ExtensionOption', 'OrderedExtensionsOption', 'ConfigurationError', 
    3031           'default_dir'] 
    3132 
     
    8990        """ 
    9091        return self[section].getint(name, default) 
    9192 
     93    def getfloat(self, section, name, default=None): 
     94        """Return the value of the specified option as a floating number. 
     95         
     96        If the specified option can not be converted to an integer, a 
     97        `ConfigurationError` exception is raised. 
     98         
     99        (since Trac 0.11) 
     100        """ 
     101        return self[section].getfloat(name, default) 
     102 
    92103    def getlist(self, section, name, default=None, sep=',', keep_empty=False): 
    93104        """Return a list of values that have been specified as a single 
    94105        comma-separated option. 
     
    261272        except ValueError: 
    262273            raise ConfigurationError('expected integer, got %s' % repr(value)) 
    263274 
     275    def getfloat(self, name, default=None): 
     276        """Return the value of the specified option as a floating number. 
     277         
     278        If the specified option can not be converted to a float, a 
     279        `ConfigurationError` exception is raised. 
     280        """ 
     281        value = self.get(name, default) 
     282        if value == '': 
     283            return default 
     284        try: 
     285            return float(value) 
     286        except ValueError: 
     287            raise ConfigurationError('expected float, got %s' % repr(value)) 
     288 
    264289    def getlist(self, name, default=None, sep=',', keep_empty=True): 
    265290        """Return a list of values that have been specified as a single 
    266291        comma-separated option. 
     
    346371    """Descriptor for integer configuration options.""" 
    347372    accessor = Section.getint 
    348373 
     374class FloatOption(Option): 
     375    """Descriptor for floating-point numbers configuration options.""" 
     376    accessor = Section.getfloat 
    349377 
    350378class ListOption(Option): 
    351379    """Descriptor for configuration options that contain multiple values