\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
\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
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
\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
}\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
}\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
}\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
"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
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
--- /dev/null
+/*\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
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
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