Merge branch 'hs/7873'
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index fca220f..1ca82e8 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -124,6 +124,19 @@
if (i + 1) % 100 == 0:
log.info('Compute last commit info %d: %s', (i + 1), ci._id)
+ # Clear any existing caches for branches/tags
+ if repo.cached_branches:
+ repo.cached_branches = []
+ session(repo).flush()
+
+ if repo.cached_tags:
+ repo.cached_tags = []
+ session(repo).flush()
+ # The first view can be expensive to cache,
+ # so we want to do it here instead of on the first view.
+ repo.get_branches()
+ repo.get_tags()
+
if not all_commits and not new_clone:
for commit in commit_ids:
new = repo.commit(commit)
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 022343f..a3d574c 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -349,6 +349,8 @@
repo_tags = FieldProperty(S.Deprecated)
upstream_repo = FieldProperty(dict(name=str, url=str))
default_branch_name = FieldProperty(str)
+ cached_branches = FieldProperty([dict(name=str, object_id=str)])
+ cached_tags = FieldProperty([dict(name=str, object_id=str)])
def __init__(self, **kw):
if 'name' in kw and 'tool' in kw:
diff --git a/Allura/development.ini b/Allura/development.ini
index 9146033..bb2ccab 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -297,6 +297,12 @@
scm.import.retry_count = 50
scm.import.retry_sleep_secs = 5
+; When getting a list of valid references (branches/tags) from a repo, you can cache
+; the results in mongo based on a threshold. Set `repo_refs_cache_threshold` (in seconds) and the resulting
+; lists will be cached and served from cache on subsequent requests until reset by `repo_refresh`.
+; Set to 0 to cache all references. Remove entirely to cache nothing.
+repo_refs_cache_threshold = .5
+
; One-click merge is enabled by default, but can be turned off on for each type of repo
scm.merge.git.disabled = false
scm.merge.hg.disabled = false
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 416db33..7d87a09 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -532,6 +532,45 @@
except KeyError:
return False
+ def _get_refs(self, field_name):
+ """ Returns a list of valid reference objects (branches or tags) from the git database
+
+ :return: List of git ref objects.
+ :rtype: list
+ """
+
+ cache_name = 'cached_' + field_name
+ cache = getattr(self._repo, cache_name, None)
+
+ if cache:
+ return cache
+
+ refs = []
+ start_time = time()
+ ref_list = getattr(self._git, field_name)
+ for ref in ref_list:
+ try:
+ hex_sha = ref.commit.hexsha
+ except ValueError:
+ log.debug(u"Found invalid sha: {}".format(ref))
+ continue
+ refs.append(Object(name=ref.name, object_id=hex_sha))
+ time_taken = time() - start_time
+
+ threshold = tg.config.get('repo_refs_cache_threshold')
+ try:
+ threshold = float(threshold) if threshold else None
+ except ValueError:
+ threshold = None
+ log.warn('Skipping reference caching - The value for config param '
+ '"repo_refs_cache_threshold" must be a float.')
+
+ if threshold is not None and time_taken > threshold:
+ setattr(self._repo, cache_name, refs)
+ session(self._repo).flush(self._repo)
+
+ return refs
+
@LazyProperty
def head(self):
if not self._git or not self._git.heads:
@@ -549,15 +588,15 @@
@LazyProperty
def heads(self):
- return [Object(name=b.name, object_id=b.commit.hexsha) for b in self._git.heads if b.is_valid()]
+ return self._get_refs('heads')
@LazyProperty
def branches(self):
- return [Object(name=b.name, object_id=b.commit.hexsha) for b in self._git.branches if b.is_valid()]
+ return self._get_refs('branches')
@LazyProperty
def tags(self):
- return [Object(name=t.name, object_id=t.commit.hexsha) for t in self._git.tags if t.is_valid()]
+ return self._get_refs('tags')
def set_default_branch(self, name):
if not name:
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 349efe6..c28daf1 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -728,6 +728,17 @@
res_with_tmp = self.repo.merge_request_commits(mr)
assert_equals(res_without_tmp, res_with_tmp)
+ def test_cached_branches(self):
+ with mock.patch.dict('allura.lib.app_globals.config', {'repo_refs_cache_threshold': '0'}):
+ rev = GM.Repository.query.get(_id=self.repo['_id'])
+ branches = rev._impl._get_refs('branches')
+ assert_equal(rev.cached_branches, branches)
+
+ def test_cached_tags(self):
+ with mock.patch.dict('allura.lib.app_globals.config', {'repo_refs_cache_threshold': '0'}):
+ rev = GM.Repository.query.get(_id=self.repo['_id'])
+ tags = rev._impl._get_refs('tags')
+ assert_equal(rev.cached_tags, tags)
class TestGitImplementation(unittest.TestCase):
@@ -735,6 +746,7 @@
repo_dir = pkg_resources.resource_filename(
'forgegit', 'tests/data/testgit.git')
repo = mock.Mock(full_fs_path=repo_dir)
+ repo.cached_branches = []
impl = GM.git_repo.GitImplementation(repo)
self.assertEqual(impl.branches, [
Object(name='master',
@@ -747,6 +759,7 @@
repo_dir = pkg_resources.resource_filename(
'forgegit', 'tests/data/testgit.git')
repo = mock.Mock(full_fs_path=repo_dir)
+ repo.cached_tags = []
impl = GM.git_repo.GitImplementation(repo)
self.assertEqual(impl.tags, [
Object(name='foo',