]> source.dussan.org Git - gitblit.git/commitdiff
Cache repository sizes and default metrics for performance boost
authorJames Moger <james.moger@gitblit.com>
Fri, 21 Oct 2011 21:00:07 +0000 (17:00 -0400)
committerJames Moger <james.moger@gitblit.com>
Fri, 21 Oct 2011 21:00:07 +0000 (17:00 -0400)
docs/00_index.mkd
docs/04_releases.mkd
src/com/gitblit/GitBlit.java
src/com/gitblit/models/ObjectCache.java [new file with mode: 0644]
src/com/gitblit/wicket/pages/SummaryPage.java

index ee5faafbef40c7dfbf8793eedd47369a83812903..6a2d0d18189920d79970b90bb6202ec2e587ec17 100644 (file)
@@ -31,6 +31,8 @@ Gitblit requires a Java 6 Runtime Environment (JRE) or a Java 6 Development Kit
 \r
 - improved: updated ui with Twitter's Bootstrap CSS toolkit  \r
     **New:** *web.loginMessage = gitblit*\r
+- improved: repositories list performance by caching repository sizes (issue 27)\r
+- improved: summary page performance by caching metric calculations (issue 25)\r
 - added: authenticated JSON RPC mechanism   \r
     **New:** *web.enableRpcServlet = true*  \r
     **New:** *web.enableRpcAdministration = false*\r
index fad61d3296372c1d0c4d64d2a2d9248c34415e73..ba09075fbdef739fe3b82174bb1395fe564cebfd 100644 (file)
@@ -5,6 +5,8 @@
 \r
 - improved: updated ui with Twitter's Bootstrap CSS toolkit  \r
     **New:** *web.loginMessage = gitblit*\r
+- improved: repositories list performance by caching repository sizes (issue 27)\r
+- improved: summary page performance by caching metric calculations (issue 25)\r
 - added: authenticated JSON RPC mechanism   \r
     **New:** *web.enableRpcServlet = true*  \r
     **New:** *web.enableRpcAdministration = false*\r
index 50eeb9ebddc20b122c2889c9d30281f30f6d0281..51d5612e317bfa824c8732a7e531b43db12b3bc8 100644 (file)
@@ -60,12 +60,15 @@ import com.gitblit.Constants.FederationToken;
 import com.gitblit.models.FederationModel;\r
 import com.gitblit.models.FederationProposal;\r
 import com.gitblit.models.FederationSet;\r
+import com.gitblit.models.Metric;\r
+import com.gitblit.models.ObjectCache;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ByteFormat;\r
 import com.gitblit.utils.FederationUtils;\r
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.JsonUtils;\r
+import com.gitblit.utils.MetricUtils;\r
 import com.gitblit.utils.StringUtils;\r
 \r
 /**\r
@@ -97,6 +100,10 @@ public class GitBlit implements ServletContextListener {
 \r
        private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>();\r
 \r
+       private final ObjectCache<Long> repositorySizeCache = new ObjectCache<Long>();\r
+\r
+       private final ObjectCache<List<Metric>> repositoryMetricsCache = new ObjectCache<List<Metric>>();\r
+\r
        private RepositoryResolver<Void> repositoryResolver;\r
 \r
        private File repositoriesFolder;\r
@@ -418,6 +425,16 @@ public class GitBlit implements ServletContextListener {
                }\r
        }\r
 \r
+       /**\r
+        * Clears all the cached data for the specified repository.\r
+        * \r
+        * @param repositoryName\r
+        */\r
+       public void clearRepositoryCache(String repositoryName) {\r
+               repositorySizeCache.remove(repositoryName);\r
+               repositoryMetricsCache.remove(repositoryName);\r
+       }\r
+\r
        /**\r
         * Returns the list of all repositories available to Gitblit. This method\r
         * does not consider user access permissions.\r
@@ -550,14 +567,22 @@ public class GitBlit implements ServletContextListener {
        }\r
 \r
        /**\r
-        * Returns the size in bytes of the repository.\r
+        * Returns the size in bytes of the repository. Gitblit caches the\r
+        * repository sizes to reduce the performance penalty of recursive\r
+        * calculation. The cache is updated if the repository has been changed\r
+        * since the last calculation.\r
         * \r
         * @param model\r
         * @return size in bytes\r
         */\r
        public long calculateSize(RepositoryModel model) {\r
+               if (repositorySizeCache.hasCurrent(model.name, model.lastChange)) {\r
+                       return repositorySizeCache.getObject(model.name);\r
+               }\r
                File gitDir = FileKey.resolve(new File(repositoriesFolder, model.name), FS.DETECTED);\r
-               return com.gitblit.utils.FileUtils.folderSize(gitDir);\r
+               long size = com.gitblit.utils.FileUtils.folderSize(gitDir);\r
+               repositorySizeCache.updateObject(model.name, model.lastChange, size);\r
+               return size;\r
        }\r
 \r
        /**\r
@@ -596,7 +621,26 @@ public class GitBlit implements ServletContextListener {
        }\r
 \r
        /**\r
-        * Returns the gitblit string vlaue for the specified key. If key is not\r
+        * Returns the metrics for the default branch of the specified repository.\r
+        * This method builds a metrics cache. The cache is updated if the\r
+        * repository is updated. A new copy of the metrics list is returned on each\r
+        * call so that modifications to the list are non-destructive.\r
+        * \r
+        * @param model\r
+        * @param repository\r
+        * @return a new array list of metrics\r
+        */\r
+       public List<Metric> getRepositoryDefaultMetrics(RepositoryModel model, Repository repository) {\r
+               if (repositoryMetricsCache.hasCurrent(model.name, model.lastChange)) {\r
+                       return new ArrayList<Metric>(repositoryMetricsCache.getObject(model.name));\r
+               }\r
+               List<Metric> metrics = MetricUtils.getDateMetrics(repository, null, true, null);\r
+               repositoryMetricsCache.updateObject(model.name, model.lastChange, metrics);\r
+               return new ArrayList<Metric>(metrics);\r
+       }\r
+\r
+       /**\r
+        * Returns the gitblit string value for the specified key. If key is not\r
         * set, returns defaultValue.\r
         * \r
         * @param config\r
@@ -678,6 +722,9 @@ public class GitBlit implements ServletContextListener {
                                                        "Failed to rename repository permissions ''{0}'' to ''{1}''.",\r
                                                        repositoryName, repository.name));\r
                                }\r
+\r
+                               // clear the cache\r
+                               clearRepositoryCache(repositoryName);\r
                        }\r
 \r
                        // load repository\r
@@ -758,6 +805,9 @@ public class GitBlit implements ServletContextListener {
                                        return true;\r
                                }\r
                        }\r
+\r
+                       // clear the repository cache\r
+                       clearRepositoryCache(repositoryName);\r
                } catch (Throwable t) {\r
                        logger.error(MessageFormat.format("Failed to delete repository {0}", repositoryName), t);\r
                }\r
diff --git a/src/com/gitblit/models/ObjectCache.java b/src/com/gitblit/models/ObjectCache.java
new file mode 100644 (file)
index 0000000..48ede90
--- /dev/null
@@ -0,0 +1,85 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.models;\r
+\r
+import java.io.Serializable;\r
+import java.util.Date;\r
+import java.util.Map;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+\r
+/**\r
+ * Reusable object cache.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class ObjectCache<X> implements Serializable {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final Map<String, CachedObject<X>> cache = new ConcurrentHashMap<String, CachedObject<X>>();\r
+\r
+       private class CachedObject<Y> {\r
+\r
+               public final String name;\r
+\r
+               private volatile Date date;\r
+\r
+               private volatile Y object;\r
+\r
+               CachedObject(String name) {\r
+                       this.name = name;\r
+                       date = new Date(0);\r
+               }\r
+\r
+               @Override\r
+               public String toString() {\r
+                       return getClass().getSimpleName() + ": " + name;\r
+               }\r
+       }\r
+\r
+       public boolean hasCurrent(String name, Date date) {\r
+               return cache.containsKey(name) && cache.get(name).date.compareTo(date) == 0;\r
+       }\r
+\r
+       public Date getDate(String name) {\r
+               return cache.get(name).date;\r
+       }\r
+\r
+       public X getObject(String name) {\r
+               return cache.get(name).object;\r
+       }\r
+\r
+       public void updateObject(String name, X object) {\r
+               this.updateObject(name, new Date(), object);\r
+       }\r
+\r
+       public void updateObject(String name, Date date, X object) {\r
+               CachedObject<X> obj;\r
+               if (cache.containsKey(name)) {\r
+                       obj = cache.get(name);\r
+               } else {\r
+                       obj = new CachedObject<X>(name);\r
+                       cache.put(name, obj);\r
+               }\r
+               obj.date = date;\r
+               obj.object = object;\r
+       }\r
+\r
+       public Object remove(String name) {\r
+               return cache.remove(name).object;\r
+       }\r
+}\r
index 9fd90c5f221110c0d2c40847e605a54857c59423..50810a182932e411f5be502e1e504a5f706e4ff0 100644 (file)
@@ -45,7 +45,6 @@ import com.gitblit.models.Metric;
 import com.gitblit.models.PathModel;\r
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.MarkdownUtils;\r
-import com.gitblit.utils.MetricUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.utils.TimeUtils;\r
 import com.gitblit.wicket.WicketUtils;\r
@@ -68,7 +67,7 @@ public class SummaryPage extends RepositoryPage {
                List<Metric> metrics = null;\r
                Metric metricsTotal = null;\r
                if (GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {\r
-                       metrics = MetricUtils.getDateMetrics(r, null, true, null);\r
+                       metrics = GitBlit.self().getRepositoryDefaultMetrics(getRepositoryModel(), r);\r
                        metricsTotal = metrics.remove(0);\r
                }\r
 \r