From 989a7595bd9240dcf67d76b5779d12739a00c747 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 12 Aug 2014 09:02:31 +0200 Subject: [PATCH] improve performance for simple subversion changeset paging --- .../scm/repository/spi/SvnLogCommand.java | 271 +++++++++++++----- 1 file changed, 201 insertions(+), 70 deletions(-) diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java index df9fc9f76f..b17c507d5a 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java @@ -41,6 +41,7 @@ import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.io.SVNRepository; @@ -114,21 +115,16 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand try { - long revisioNumber = Long.parseLong(revision); - SVNRepository repository = open(); - Collection entries = repository.log(null, null, - revisioNumber, revisioNumber, true, - true); + long revisioNumber = parseRevision(revision); + SVNRepository repo = open(); + Collection entries = repo.log(null, null, revisioNumber, + revisioNumber, true, true); if (Util.isNotEmpty(entries)) { changeset = SvnUtil.createChangeset(entries.iterator().next()); } } - catch (NumberFormatException ex) - { - throw new RepositoryException("could not convert revision", ex); - } catch (SVNException ex) { throw new RepositoryException("could not open repository", ex); @@ -158,75 +154,30 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand } ChangesetPagingResult changesets = null; - String startRevision = request.getStartChangeset(); - String endRevision = request.getEndChangeset(); + int start = request.getPagingStart(); + int limit = request.getPagingLimit(); + long startRevision = parseRevision(request.getStartChangeset()); + long endRevision = parseRevision(request.getEndChangeset()); + String[] pathArray = null; + + if (!Strings.isNullOrEmpty(request.getPath())) + { + pathArray = new String[] { request.getPath() }; + } try { - SVNRepository repository = open(); - long startRev = repository.getLatestRevision(); - long endRev = 0; - long maxRev = startRev; + SVNRepository repo = open(); - if (!Strings.isNullOrEmpty(startRevision)) + if ((startRevision > 0) || (pathArray != null)) { - startRev = Long.parseLong(startRevision); + changesets = getChangesets(repo, startRevision, endRevision, start, + limit, pathArray); } - - if (!Strings.isNullOrEmpty(endRevision)) + else { - endRev = Long.parseLong(endRevision); + changesets = getChangesets(repo, start, limit); } - - String[] pathArray = null; - - if (!Strings.isNullOrEmpty(request.getPath())) - { - pathArray = new String[] { request.getPath() }; - } - - List changesetList = Lists.newArrayList(); - Collection entries = repository.log(pathArray, null, - startRev, endRev, true, true); - - for (SVNLogEntry entry : entries) - { - if (entry.getRevision() <= maxRev) - { - changesetList.add(entry); - } - } - - int total = changesetList.size(); - int start = request.getPagingStart(); - int max = request.getPagingLimit() + start; - int end = total; - - if ((max > 0) && (end > max)) - { - end = max; - } - - if (start < 0) - { - start = 0; - } - - if (logger.isTraceEnabled()) - { - logger.trace( - "create sublist from {} to {} of total {} collected changesets", - start, end, total); - } - - changesetList = changesetList.subList(start, end); - changesets = new ChangesetPagingResult(total, - SvnUtil.createChangesets(changesetList)); - } - catch (NumberFormatException ex) - { - throw new RepositoryException( - "could not parse revision ".concat(startRevision), ex); } catch (SVNException ex) { @@ -235,4 +186,184 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand return changesets; } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param v + * + * @return + * + * @throws RepositoryException + */ + private long parseRevision(String v) throws RepositoryException + { + long result = -1l; + + if (!Strings.isNullOrEmpty(v)) + { + try + { + result = Long.parseLong(v); + } + catch (NumberFormatException ex) + { + throw new RepositoryException( + String.format("could not convert revision %s", v), ex); + } + } + + return result; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repo + * @param start + * @param limit + * + * @return + * + * @throws SVNException + */ + private ChangesetPagingResult getChangesets(SVNRepository repo, int start, + int limit) + throws SVNException + { + long latest = repo.getLatestRevision(); + long startRev = latest - start; + long endRev = Math.max(startRev - (limit - 1), 0); + + final List changesets = Lists.newArrayList(); + + if (startRev > 0) + { + logger.debug("fetch changeset from {} to {}", startRev, endRev); + repo.log(null, startRev, endRev, true, true, + new ChangesetCollector(changesets)); + } + + return new ChangesetPagingResult((int) (latest + 1l), changesets); + } + + /** + * Method description + * + * + * @param repo + * @param startRevision + * @param endRevision + * @param start + * @param limit + * @param path + * + * @return + * + * @throws SVNException + */ + private ChangesetPagingResult getChangesets(SVNRepository repo, + long startRevision, long endRevision, int start, int limit, String[] path) + throws SVNException + { + long startRev; + long endRev = Math.max(endRevision, 0); + long maxRev = repo.getLatestRevision(); + + if (startRevision >= 0l) + { + startRev = startRevision; + } + else + { + startRev = maxRev; + } + + List changesetList = Lists.newArrayList(); + + logger.debug("fetch changeset from {} to {} for path {}", startRev, endRev, + path); + + Collection entries = repo.log(path, null, startRev, endRev, + true, true); + + for (SVNLogEntry entry : entries) + { + if (entry.getRevision() <= maxRev) + { + changesetList.add(entry); + } + } + + int total = changesetList.size(); + int max = limit + start; + int end = total; + + if ((max > 0) && (end > max)) + { + end = max; + } + + if (start < 0) + { + start = 0; + } + + logger.trace( + "create sublist from {} to {} of total {} collected changesets", start, + end, total); + + changesetList = changesetList.subList(start, end); + + return new ChangesetPagingResult(total, + SvnUtil.createChangesets(changesetList)); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Collect and convert changesets. + * + */ + private static class ChangesetCollector implements ISVNLogEntryHandler + { + + /** + * Constructs ... + * + * + * @param changesets + */ + public ChangesetCollector(Collection changesets) + { + this.changesets = changesets; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param logEntry + * + * @throws SVNException + */ + @Override + public void handleLogEntry(SVNLogEntry logEntry) throws SVNException + { + changesets.add(SvnUtil.createChangeset(logEntry)); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private final Collection changesets; + } }