Hello,

There's a patch I'd like to commit on stable before 0.11.1, but first 
I'd like to check if nobody foresees a problem with this, and also if I 
could get some testing feedback, particularly from people still 
experiencing db timeout errors.

This patch should improve upon concurrency and reduce the risks for a 
timeout while waiting for a db connection, when Trac is used with a 
cached svn repository (i.e. probably still the typical situation).

Up to now for those setups, every request initializes a CachedRepository 
object which will only be freed at the end of the request (during 
shutdown(tid)). Since a db connection is stored there, that connection 
can't go back to the pool even if it's not used and so it cannot be 
reused by other requests.

The proposed patch fixes this. It's done in a backward compatible way, 
in case some other vc backends are reusing the CachedRepository.

It'd be nice if jonas could try it on some heavily used Trac ;-)

-- Christian

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Trac 
Development" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/trac-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Index: trac/versioncontrol/svn_fs.py
===================================================================
--- trac/versioncontrol/svn_fs.py       (revision 7355)
+++ trac/versioncontrol/svn_fs.py       (working copy)
@@ -278,7 +278,7 @@
         if type == 'direct-svnfs':
             repos = fs_repos
         else:
-            repos = CachedRepository(self.env.get_db_cnx(), fs_repos, None,
+            repos = CachedRepository(self.env.get_db_cnx, fs_repos, None,
                                      self.log)
             repos.has_linear_changesets = True
         if authname:
Index: trac/versioncontrol/cache.py
===================================================================
--- trac/versioncontrol/cache.py        (revision 7355)
+++ trac/versioncontrol/cache.py        (working copy)
@@ -39,9 +39,12 @@
 
     has_linear_changesets = False
 
-    def __init__(self, db, repos, authz, log):
+    def __init__(self, getdb, repos, authz, log):
         Repository.__init__(self, repos.name, authz, log)
-        self.db = db
+        if callable(getdb):
+            self.getdb = getdb
+        else:
+            self.getdb = lambda: getdb
         self.repos = repos
 
     def close(self):
@@ -53,10 +56,11 @@
 
     def get_changeset(self, rev):
         return CachedChangeset(self.repos, self.repos.normalize_rev(rev),
-                               self.db, self.authz)
+                               self.getdb, self.authz)
 
     def get_changesets(self, start, stop):
-        cursor = self.db.cursor()
+        db = self.getdb()
+        cursor = db.cursor()
         cursor.execute("SELECT rev FROM revision "
                        "WHERE time >= %s AND time < %s "
                        "ORDER BY time DESC, rev DESC",
@@ -70,16 +74,17 @@
 
     def sync_changeset(self, rev):
         cset = self.repos.get_changeset(rev)
-        cursor = self.db.cursor()
+        db = self.getdb()
+        cursor = self.cursor()
         cursor.execute("UPDATE revision SET time=%s, author=%s, message=%s "
                        "WHERE rev=%s", (to_timestamp(cset.date),
                                         cset.author, cset.message,
                                         (str(cset.rev))))
-        self.db.commit()
+        db.commit()
         
     def sync(self, feedback=None):
-        cursor = self.db.cursor()
-
+        db = self.getdb()
+        cursor = db.cursor()
         cursor.execute("SELECT name, value FROM system WHERE name IN (%s)" %
                        ','.join(["'%s'" % key for key in CACHE_METADATA_KEYS]))
         metadata = {}
@@ -103,7 +108,7 @@
             cursor.execute("UPDATE system SET value=%s WHERE name=%s",
                            (self.name, CACHE_REPOSITORY_DIR))
 
-        self.db.commit() # save metadata changes made up to now
+        db.commit() # save metadata changes made up to now
 
         # -- retrieve the youngest revision in the repository
         self.repos.clear()
@@ -186,7 +191,7 @@
                         # also potentially in progress, so keep ''previous''
                         # notion of 'youngest'
                         self.repos.clear(youngest_rev=self.youngest)
-                        self.db.rollback()
+                        db.rollback()
                         return
 
                     # 1.2. now *only* one process was able to get there
@@ -213,7 +218,7 @@
                     #      (minimize possibility of failures at point 0.)
                     cursor.execute("UPDATE system SET value=%s WHERE name=%s",
                                    (str(self.youngest), CACHE_YOUNGEST_REV))
-                    self.db.commit()
+                    db.commit()
 
                     # 1.5. provide some feedback
                     if feedback:
@@ -249,9 +254,10 @@
             return self.repos.next_rev(rev, path)
 
     def _next_prev_rev(self, direction, rev, path=''):
+        db = self.getdb()
         # the changeset revs are sequence of ints:
         sql = "SELECT rev FROM node_change WHERE " + \
-              self.db.cast('rev', 'int') + " " + direction + " %s"
+              db.cast('rev', 'int') + " " + direction + " %s"
         args = [rev]
 
         if path:
@@ -262,8 +268,8 @@
             args.append(path)
             sql += " OR "
             # changes on path children
-            sql += "path "+self.db.like()
-            args.append(self.db.like_escape(path+'/') + '%')
+            sql += "path "+db.like()
+            args.append(db.like_escape(path+'/') + '%')
             sql += " OR "
             # deletion of path ancestors
             components = path.lstrip('/').split('/')
@@ -273,10 +279,10 @@
             sql += " (path in (" + parent_insert + ") and change_type='D')"
             sql += ")"
 
-        sql += " ORDER BY " + self.db.cast('rev', 'int') + \
+        sql += " ORDER BY " + db.cast('rev', 'int') + \
                 (direction == '<' and " DESC" or "") + " LIMIT 1"
         
-        cursor = self.db.cursor()
+        cursor = db.cursor()
         cursor.execute(sql, args)
         for rev, in cursor:
             return rev
@@ -301,11 +307,12 @@
 
 class CachedChangeset(Changeset):
 
-    def __init__(self, repos, rev, db, authz):
+    def __init__(self, repos, rev, getdb, authz):
         self.repos = repos
-        self.db = db
+        self.getdb = getdb
         self.authz = authz
-        cursor = self.db.cursor()
+        db = self.getdb()
+        cursor = db.cursor()
         cursor.execute("SELECT time,author,message FROM revision "
                        "WHERE rev=%s", (str(rev),))
         row = cursor.fetchone()
@@ -318,7 +325,8 @@
         self.scope = getattr(repos, 'scope', '')
 
     def get_changes(self):
-        cursor = self.db.cursor()
+        db = self.getdb()
+        cursor = db.cursor()
         cursor.execute("SELECT path,node_type,change_type,base_path,base_rev "
                        "FROM node_change WHERE rev=%s "
                        "ORDER BY path", (str(self.rev),))

Reply via email to