Hi,

On Sun, Aug 11, 2013 at 5:29 AM, Eli Carter <[email protected]> wrote:
> part3: uncache previous_rev()
>     Makes previous_rev() unconditionally go to the repository without
> hitting the node_change table.  (Was "part3a".)
>
> part4: warn on slow next_rev()
>     This logs a warning if next_rev() takes more than 10 seconds to
> complete, suggesting enabling [trac] debug_sql.  (Was "part3b".)
>     The purpose of this is to give the admin that first breadcrumb to
> lead them to the underlying cause of their slowness.

I tried performance tuning of `_next_prev_rev`, tuning-_next_prev_rev.diff.

 a. If the node is a directory, don't check changes of node children.

 b. In condition for deletion of path ancestors, first check change_type.
    The check for `path IN (...)` is expensive, if the path is deep.

 c. In condition for deletion of path ancestors, don't check path itself.
    The checking of path itself is included in the following.

            # changes on path itself
            sql += " AND (path=%s"
            args.append(path)


Timing of `_next_prev_rev` with source:trunk/tracopt/mimeview/tests/php.py@8802
of a clone of http://svn.edgewall.com/repos/trac.

|| Database          ||   before             ||     after            ||
||                   ||   <      ||   >      ||   <      ||   >      || 
direction parameter
|| SQLite 3.3.6      || 0.209179s|| 0.045196s|| 0.103258s|| 0.024124s||
|| MySQL 5.0.95      || 0.230615s|| 0.236727s|| 0.203341s|| 0.214520s|| 
innodb_buffer_pool_size = 32M
|| PostgreSQL 8.1.23 || 0.085839s|| 0.023007s|| 0.072130s|| 0.018516s||


Thoughts?


BTW, I think that the condition for deletion of path ancestors should also
check the move of path ancestors.

    change_type IN ('D', 'M')
instead of
    change_type='D'

-- 
Jun Omae <[email protected]> (大前 潤)

-- 
You received this message because you are subscribed to the Google Groups "Trac 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/trac-dev.
For more options, visit https://groups.google.com/groups/opt_out.
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
index 9967d99..a6e5195 100644
--- a/trac/versioncontrol/cache.py
+++ b/trac/versioncontrol/cache.py
@@ -20,7 +20,8 @@ from trac.cache import cached
 from trac.core import TracError
 from trac.util.datefmt import from_utimestamp, to_utimestamp
 from trac.util.translation import _
-from trac.versioncontrol import Changeset, Node, Repository, NoSuchChangeset
+from trac.versioncontrol.api import Changeset, Node, Repository, \
+                                    NoSuchChangeset, NoSuchNode
 
 
 _kindmap = {'D': Node.DIRECTORY, 'F': Node.FILE}
@@ -361,28 +362,41 @@ class CachedRepository(Repository):
             return self.repos.next_rev(self.normalize_rev(rev), path)
 
     def _next_prev_rev(self, direction, rev, path=''):
+        path = self.normalize_path(path)
         srev = self.db_rev(rev)
-        db = self.env.get_db_cnx()
+        db = self.env.get_read_db()
         # the changeset revs are sequence of ints:
         sql = "SELECT rev FROM node_change WHERE repos=%s AND " + \
               "rev" + direction + "%s"
         args = [self.id, srev]
 
+        path = path.lstrip('/')
         if path:
-            path = path.lstrip('/')
-            # changes on path itself or its children
-            sql += " AND (path=%s OR path " + db.like()
-            args.extend((path, db.like_escape(path + '/') + '%'))
-            # deletion of path ancestors
-            components = path.lstrip('/').split('/')
-            parents = ','.join(('%s',) * len(components))
-            sql += " OR (path IN (" + parents + ") AND change_type='D'))"
-            for i in range(1, len(components) + 1):
-                args.append('/'.join(components[:i]))
+            # changes on path itself
+            sql += " AND (path=%s"
+            args.append(path)
+            # changes on path children if a directory
+            try:
+                node = self.get_node(path, rev)
+            except NoSuchNode:
+                # if the node is not existent, don't check the children
+                # because changes on path itself are expected
+                node = None
+            if node and node.isdir:
+                sql += " OR path " + db.like()
+                args.append(db.like_escape(path + '/') + '%')
+            # deletion of path ancestors except itself
+            components = path.split('/')[:-1]
+            if components:
+                parents = ','.join(('%s',) * len(components))
+                sql += " OR (change_type='D' AND path IN (" + parents + "))"
+                for i in range(1, len(components) + 1):
+                    args.append('/'.join(components[:i]))
+            sql += ")"
 
         sql += " ORDER BY rev" + (direction == '<' and " DESC" or "") \
                + " LIMIT 1"
-        
+
         cursor = db.cursor()
         cursor.execute(sql, args)
         for rev, in cursor:

Reply via email to