Uploaded image for project: 'Subversion'
  1. Subversion
  2. SVN-4269

subtree mergeinfo can degrade automatic merge performance




      hwright reported a run-of-the-mill automatic merge which took several minutes
      before anything appeared to happen, see
      I analyzed this merge and there is a potential performance problem when
      automatic (a.k.a. sync merges) are made to long-lived branches where a previous
      automatic merge *added* a subtree with explicit mergeinfo.  If this earlier
      merge took place long after the creation of the branch and after other sync
      merges have already occurred, then a problem can arise.
      The problem is that the merge code (specifically
      libsvn_client/merge.c:remove_noop_subtree_ranges) uses svn_ra_get_log2 to detect
      revisions that don't need to be merged because they are inoperative.  In the
      aforementioned scenario, there is the possibility that a very large number of
      logs will need to be retrieved.
      A look at the example hwright encountered will help explain.
      The merge is into a working copy of ^/subversion/branches/ev2-export.  This
      branch was made from ^/subversion/trunk@1231318.  Frequent auto merges were made
      to this branch from ^/subversion/trunk to keep it in sync.  The problem starts
      in the auto merge made in r1308894.  That merge added the subtree
      'tools/dist/make-deps-tarball.sh' which had explicit mergeinfo on it:
      C:\SVN\src-branch-ev2-export>svn pg svn:mergeinfo -r1308894
      Notice that mergeinfo describing the merge that added this subtree is recorded.
      Regular auto merges continue to be made up until today, leaving us with this
      before hwright's problematic merge:
           |             | | |      |         |      | | |
          copy           auto      auto      auto    auto
           |             merges    merge     merge   merges
           |             | | |      |         |      | | |
           v             v v v      v         v      v v v
                                           Adds subtree
                                           with explicit mergeinfo on it
      Which brings us to the merge.  We have a WC of the ev2-export branch at revision
      1417130, with this mergeinfo:
      C:\SVN\src-branch-ev2-export>svn pg svn:mergeinfo -vR
      Properties on '.':
      Properties on 'tools\dist\make-deps-tarball.sh':
      What we want to pay close attention to is the mergeinfo from ^/subversion/trunk
      (since that is where we will merge from):
      Properties on '.':
      Properties on 'tools\dist\make-deps-tarball.sh':
      When we try an auto merge like so,
           |             | | |      |         |      | | |      |
          copy           auto      auto      auto    auto      auto
           |             merges    merge     merge   merges    merge
           |             | | |      |         |      | | |      |
           v             v v v      v         v      v v v      v 
                                           Adds subtree
                                           with explicit mergeinfo on it
      the merge code starts by assuming that -r1231318(YCA):1417138(HEAD) needs to be
      merged.  It then tries to figure out what part of this range is already merged
      per the merge target's mergeinfo, both that on the target and on any of its
      subtrees.  The code does several calculations in this space, but the one that is
      causing the problem is in libsvn_client/merge.c:remove_noop_subtree_ranges(),
      where we determine what revisions are needed by subtrees but aren't required by
      the target itself.  Coming into this function we've already pared down our
      initial default merge assumption:
      FOR THE TARGET '.':
        Default Merge:  /subversion/trunk:1231319:1417138
        Less Mergeinfo: /subversion/trunk:1231319-1295003,1295006-1416744
        To be merged:   /subversion/trunk:1416745-1417138*
      * The gap 1295004-1295005 represents a replacement of trunk with itself, this
      has already been removed.
      FOR THE SUBTREE 'tools\dist\make-deps-tarball.sh':
        Default Merge:  /subversion/trunk/tools/dist/make-deps-tarball.sh:1231319:1417138
        Less Mergeinfo: /subversion/trunk/tools/dist/make-deps-tarball.sh:1304315-1404840
        To be merged:  
      That's what we know when we enter remove_noop_subtree_ranges():
      /* Helper for do_directory_merge().
         SOURCE is cascaded from the argument of the same name in
         do_directory_merge().  TARGET is the merge target.  RA_SESSION is the
         session for SOURCE->loc2.
         Find all the ranges required by subtrees in
         CHILDREN_WITH_MERGEINFO that are *not* required by
         TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]).  If such
         ranges exist, then find any subset of ranges which, if merged, would be
         inoperative.  Finally, if any inoperative ranges are found then remove
         these ranges from all of the subtree's REMAINING_RANGES.
         This function should only be called when honoring mergeinfo during
         forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
      static svn_error_t *
      remove_noop_subtree_ranges(const merge_source_t *source,
                                 const merge_target_t *target,
                                 svn_ra_session_t *ra_session,
                                 apr_array_header_t *children_with_mergeinfo,
                                 apr_pool_t *result_pool,
                                 apr_pool_t *scratch_pool)
      First the function determines what ranges need to be merged to the subtree but
      not the target, based on a naive reading of the mergeinfo:
        To be merged to target:  /subversion/trunk: 1416745-1417138
        To be merged to subtree:
        To be merged only to subtree: 1231319-1304314,1404841-1416744
      Then the function uses svn_ra_get_log2 (via its get_log helper) to figure out
      which of these revisions is actually operative:
        SVN_ERR(svn_ra_get_log2(ra_session, log_targets,
                                0 /* limit */, discover_changed_paths,
                                FALSE /* strict_node_history */,
                                FALSE /* include_merged_revisions */,
                                revprops, receiver, receiver_baton, pool));
      Unfortunately in this case we have a *lot* of revisions to check; there are 3412
      operative revisions here.  This takes several minutes for me against
      svn.apache.org, which represents the bulk of the delay I reported in
      http://svn.haxx.se/dev/archive-2012-12/0105.shtml.  Worse, at this point in the
      merge no progress messages have been sent, so the user simply sees this:
      C:\SVN\src-branch-ev2-export>svn merge
      https://svn.apache.org/repos/asf/subversion/trunk .
      <several minutes with no feedback>


          Issue Links



              • Assignee:
                pburba Paul Burba
              • Votes:
                0 Vote for this issue
                0 Start watching this issue


                • Created: