On Mon, Nov 8, 2010 at 1:23 PM, Paul Burba <ptbu...@gmail.com> wrote: > On Mon, Nov 8, 2010 at 10:35 AM, Paul Burba <ptbu...@gmail.com> wrote: >> On Mon, Nov 8, 2010 at 9:13 AM, Paul Burba <ptbu...@gmail.com> wrote: >>> On Thu, Nov 4, 2010 at 8:59 PM, Chris Tashjian <ct...@thepond.com> wrote: >>>> I posted this on the users list, but I'm confident that this is a bug. >>>> >>>> Background: >>>> For a while now (off and on for over a year, but more frequently in the >>>> last >>>> 6+ months) we've been having problems with svn "crashing", yet there's no >>>> error in the log file. In talking to someone the users list it sounds like >>>> svn is actually just hanging. Clients get the following response: >>>> >>>> svn: Can't connect to host 'svn': No connection could be made >>>> because the target machine actively refused it. >>>> >>>> Our repository has 129K revisions. The format is "4 layout linear", it was >>>> created with svnadmin 1.4.x and has since had "svnadmin upgrade" run both >>>> in >>>> 1.5 and 1.6. We're currently running SlikSVN 1.6.13 (Win32), but I have >>>> previously had this problem dating back to versions of 1.5, both stock and >>>> from CollabNet. The issue now happens numerous times per day and it looks >>>> like I've discovered why.... >>>> >>>> >>>> As a test I ran "svn blame -g" on a file with a bunch of revisions and >>>> watched memory usage on the server spin up to 2GB. >>> >>> Chris, >>> >>> By a "bunch of revisions" what exactly do you mean? Many revisions >>> which were the result of a merge? Or simply many changes made >>> directly to the file (not the result of a merge)? >>> >>>> Paul - I'll see if I can get a test repo up with the error. In the >>>> meantime, would a copy of the svn:mergeinfo help? >>> >>> Any luck? >> >> Chris, >> >> Don't sweat the replication effort too much, I think I'm on the trail >> of this problem. Using a copy of the old (pre-ASF) Subversion >> repository I'm seeing unexpectedly high memory use when using log -g >> on a file with a "lot" of merge history: >> >> 1.6.13.client>svn blame ^/trunk/subversion/tests/cmdline/merge_tests.py >> 25 MB Peak Working Set Memory svnserve.exe >> >> 1.6.13.client>svn blame ^/trunk/subversion/tests/cmdline/merge_tests.py -g >> 291 MB Peak Working Set Memory svnserve.exe > ^^^ > That should have been 691 MB! Sorry! > >> >> More soon...
Hi Chris, Ok, I think I got it. Switching to a Subversion tr...@1032651 (debug) build and using this repository as a test*: http://svn.collab.net/tmp/subversion-data-backup/subversion-history-20091115.tar.bz2 The peak working set memory of 'svn blame ^/trunk/subversion/tests/cmdline/merge_tests.py' was 21 MB 'svn blame ^/trunk/subversion/tests/cmdline/merge_tests.py -g' was 574 MB That's comparable to what you saw with 1.6.13. Applying some standard pool-fu to libsvn_repos/rev_hunt.c:(find_merged_revisions|find_interesting_revisions), see attached patch, and things look a *lot* better: The peak working set memory of 'svn blame ^/trunk/subversion/tests/cmdline/merge_tests.py' stays at 21 MB 'svn blame ^/trunk/subversion/tests/cmdline/merge_tests.py -g' drops to 71 MB A tidy 88% reduction in peak memory usage! Running the test suite on this patch. Assuming no problems I will be committing and nominating for backport to 1.6.x. log: [[[ Fix blame -g server-side memory leaks. See http://svn.haxx.se/dev/archive-2010-11/0102.shtml * subversion/libsvn_repos/rev_hunt.c (find_interesting_revisions): Implement result/scratch two-pool paradigm. Don't needlessly allocate path_revision structures until we are sure we are going to keep it. Rename local variable "iter_pool" to the de facto standard "iterpool". (find_merged_revisions): Use a separate iterpool for each nested loop. Update call to find_interesting_revisions, passing, you guessed it, an iterpool. Rename local variable "iter_pool" to the de facto standard "iterpool". ]]] Paul * This is the old Tigris Subversion repository, see http://svn.apache.org/repos/asf/subversion/README.
Index: subversion/libsvn_repos/rev_hunt.c =================================================================== --- subversion/libsvn_repos/rev_hunt.c (revision 1032800) +++ subversion/libsvn_repos/rev_hunt.c (working copy) @@ -1085,47 +1085,49 @@ apr_hash_t *duplicate_path_revs, svn_repos_authz_func_t authz_read_func, void *authz_read_baton, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *iter_pool, *last_pool; + apr_pool_t *iterpool, *last_pool; svn_fs_history_t *history; svn_fs_root_t *root; svn_node_kind_t kind; /* We switch between two pools while looping, since we need information from the last iteration to be available. */ - iter_pool = svn_pool_create(pool); - last_pool = svn_pool_create(pool); + iterpool = svn_pool_create(result_pool); + last_pool = svn_pool_create(result_pool); /* The path had better be a file in this revision. */ - SVN_ERR(svn_fs_revision_root(&root, repos->fs, end, last_pool)); - SVN_ERR(svn_fs_check_path(&kind, root, path, pool)); + SVN_ERR(svn_fs_revision_root(&root, repos->fs, end, scratch_pool)); + SVN_ERR(svn_fs_check_path(&kind, root, path, scratch_pool)); if (kind != svn_node_file) return svn_error_createf (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file in revision %ld"), path, end); /* Open a history object. */ - SVN_ERR(svn_fs_node_history(&history, root, path, last_pool)); - + SVN_ERR(svn_fs_node_history(&history, root, path, scratch_pool)); while (1) { - struct path_revision *path_rev = apr_palloc(pool, sizeof(*path_rev)); + struct path_revision *path_rev; + svn_revnum_t tmp_revnum; + const char *tmp_path; apr_pool_t *tmp_pool; - svn_pool_clear(iter_pool); + svn_pool_clear(iterpool); /* Fetch the history object to walk through. */ - SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iter_pool)); + SVN_ERR(svn_fs_history_prev(&history, history, TRUE, iterpool)); if (!history) break; - SVN_ERR(svn_fs_history_location(&path_rev->path, &path_rev->revnum, - history, iter_pool)); + SVN_ERR(svn_fs_history_location(&tmp_path, &tmp_revnum, + history, iterpool)); /* Check to see if we already saw this path (and it's ancestors) */ if (include_merged_revisions - && is_path_in_hash(duplicate_path_revs, path_rev->path, - path_rev->revnum, iter_pool)) + && is_path_in_hash(duplicate_path_revs, tmp_path, + tmp_revnum, iterpool)) break; /* Check authorization. */ @@ -1134,21 +1136,24 @@ svn_boolean_t readable; svn_fs_root_t *tmp_root; - SVN_ERR(svn_fs_revision_root(&tmp_root, repos->fs, path_rev->revnum, - iter_pool)); - SVN_ERR(authz_read_func(&readable, tmp_root, path_rev->path, - authz_read_baton, iter_pool)); + SVN_ERR(svn_fs_revision_root(&tmp_root, repos->fs, tmp_revnum, + iterpool)); + SVN_ERR(authz_read_func(&readable, tmp_root, tmp_path, + authz_read_baton, iterpool)); if (! readable) break; } - path_rev->path = apr_pstrdup(pool, path_rev->path); + /* We didn't break, so we must really want this path-rev. */ + path_rev = apr_palloc(result_pool, sizeof(*path_rev)); + path_rev->path = apr_pstrdup(result_pool, tmp_path); + path_rev->revnum = tmp_revnum; path_rev->merged = mark_as_merged; APR_ARRAY_PUSH(path_revisions, struct path_revision *) = path_rev; if (include_merged_revisions) SVN_ERR(get_merged_mergeinfo(&path_rev->merged_mergeinfo, repos, - path_rev, pool)); + path_rev, result_pool)); else path_rev->merged_mergeinfo = NULL; @@ -1156,7 +1161,7 @@ occurrences of it. We only care about this if including merged revisions, 'cause that's the only time we can have duplicates. */ apr_hash_set(duplicate_path_revs, - apr_psprintf(pool, "%s:%ld", path_rev->path, + apr_psprintf(result_pool, "%s:%ld", path_rev->path, path_rev->revnum), APR_HASH_KEY_STRING, (void *)0xdeadbeef); @@ -1164,12 +1169,12 @@ break; /* Swap pools. */ - tmp_pool = iter_pool; - iter_pool = last_pool; + tmp_pool = iterpool; + iterpool = last_pool; last_pool = tmp_pool; } - svn_pool_destroy(iter_pool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -1198,12 +1203,12 @@ { const apr_array_header_t *old; apr_array_header_t *new; - apr_pool_t *iter_pool, *last_pool; + apr_pool_t *iterpool, *last_pool; apr_array_header_t *merged_path_revisions = apr_array_make(pool, 0, sizeof(struct path_revision *)); old = mainline_path_revisions; - iter_pool = svn_pool_create(pool); + iterpool = svn_pool_create(pool); last_pool = svn_pool_create(pool); do @@ -1211,27 +1216,34 @@ int i; apr_pool_t *temp_pool; - svn_pool_clear(iter_pool); - new = apr_array_make(iter_pool, 0, sizeof(struct path_revision *)); + svn_pool_clear(iterpool); + new = apr_array_make(iterpool, 0, sizeof(struct path_revision *)); /* Iterate over OLD, checking for non-empty mergeinfo. If found, gather path_revisions for any merged revisions, and store those in NEW. */ for (i = 0; i < old->nelts; i++) { + apr_pool_t *iterpool2; apr_hash_index_t *hi; struct path_revision *old_pr = APR_ARRAY_IDX(old, i, struct path_revision *); if (!old_pr->merged_mergeinfo) continue; + iterpool2 = svn_pool_create(iterpool); + /* Determine and trace the merge sources. */ - for (hi = apr_hash_first(iter_pool, old_pr->merged_mergeinfo); hi; + for (hi = apr_hash_first(iterpool, old_pr->merged_mergeinfo); hi; hi = apr_hash_next(hi)) { + apr_pool_t *iterpool3; apr_array_header_t *rangelist; const char *path; int j; + svn_pool_clear(iterpool2); + iterpool3 = svn_pool_create(iterpool2); + apr_hash_this(hi, (void *) &path, NULL, (void *) &rangelist); for (j = 0; j < rangelist->nelts; j++) @@ -1241,9 +1253,10 @@ svn_node_kind_t kind; svn_fs_root_t *root; + svn_pool_clear(iterpool3); SVN_ERR(svn_fs_revision_root(&root, repos->fs, range->end, - iter_pool)); - SVN_ERR(svn_fs_check_path(&kind, root, path, iter_pool)); + iterpool3)); + SVN_ERR(svn_fs_check_path(&kind, root, path, iterpool3)); if (kind != svn_node_file) continue; @@ -1253,20 +1266,23 @@ TRUE, TRUE, duplicate_path_revs, authz_read_func, - authz_read_baton, pool)); + authz_read_baton, pool, + iterpool3)); } + svn_pool_destroy(iterpool3); } + svn_pool_destroy(iterpool2); } /* Append the newly found path revisions with the old ones. */ - merged_path_revisions = apr_array_append(iter_pool, merged_path_revisions, + merged_path_revisions = apr_array_append(iterpool, merged_path_revisions, new); /* Swap data structures */ old = new; temp_pool = last_pool; - last_pool = iter_pool; - iter_pool = temp_pool; + last_pool = iterpool; + iterpool = temp_pool; } while (new->nelts > 0); @@ -1277,7 +1293,7 @@ /* Copy to the output array. */ *merged_path_revisions_out = apr_array_copy(pool, merged_path_revisions); - svn_pool_destroy(iter_pool); + svn_pool_destroy(iterpool); svn_pool_destroy(last_pool); return SVN_NO_ERROR; @@ -1413,7 +1429,8 @@ SVN_ERR(find_interesting_revisions(mainline_path_revisions, repos, path, start, end, include_merged_revisions, FALSE, duplicate_path_revs, - authz_read_func, authz_read_baton, pool)); + authz_read_func, authz_read_baton, pool, + pool)); /* If we are including merged revisions, go get those, too. */ if (include_merged_revisions)