Merge ~pappacena/launchpad:refactoring-gitrepo-getRefByPath into launchpad:master

Proposed by Thiago F. Pappacena
Status: Merged
Approved by: Thiago F. Pappacena
Approved revision: 0f1b057ab34148619352d9fc33cabed2552bd619
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~pappacena/launchpad:refactoring-gitrepo-getRefByPath
Merge into: launchpad:master
Diff against target: 53 lines (+19/-5)
2 files modified
lib/lp/code/model/gitrepository.py (+7/-5)
lib/lp/code/model/tests/test_gitrepository.py (+12/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+383250@code.launchpad.net

Commit message

Refactoring GitRepository.getRefByPath method to do less database queries.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

Given that we only want the results for the first matching path, it would be possible to be more precise by doing a query that compiles down to something along the lines of this (for id == 10, path == 'master'):

  SELECT GitRef.*
  FROM GitRef, (VALUES (0, 'master'), (1, 'refs/heads/master')) AS lookup (index, path)
  WHERE GitRef.repository = 10 AND GitRef.path = lookup.path
  ORDER BY lookup.index
  LIMIT 1;

But given that there are always either one or two elements in path, it seems unlikely to be worth very much bother to fine-tune! So feel free to go ahead with your version if you want. I mention it mainly because this sort of approach can be handy in larger cases.

review: Approve
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

I like this approach too, cjwatson. I will consider it for future similar optimizations.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/model/gitrepository.py b/lib/lp/code/model/gitrepository.py
2index d88a384..416a4f3 100644
3--- a/lib/lp/code/model/gitrepository.py
4+++ b/lib/lp/code/model/gitrepository.py
5@@ -596,11 +596,13 @@ class GitRepository(StormBase, WebhookTargetMixin, GitIdentityMixin):
6 paths = [path]
7 if not path.startswith(u"refs/heads/"):
8 paths.append(u"refs/heads/%s" % path)
9- for try_path in paths:
10- ref = Store.of(self).find(
11- GitRef,
12- GitRef.repository_id == self.id,
13- GitRef.path == try_path).one()
14+ refs = Store.of(self).find(
15+ GitRef,
16+ GitRef.repository_id == self.id,
17+ GitRef.path.is_in(paths))
18+ refs_by_path = {r.path: r for r in refs}
19+ for path in paths:
20+ ref = refs_by_path.get(path)
21 if ref is not None:
22 return ref
23 return None
24diff --git a/lib/lp/code/model/tests/test_gitrepository.py b/lib/lp/code/model/tests/test_gitrepository.py
25index 8a9bcc8..165980b 100644
26--- a/lib/lp/code/model/tests/test_gitrepository.py
27+++ b/lib/lp/code/model/tests/test_gitrepository.py
28@@ -170,6 +170,7 @@ from lp.testing import (
29 logout,
30 person_logged_in,
31 record_two_runs,
32+ StormStatementRecorder,
33 TestCaseWithFactory,
34 verifyObject,
35 )
36@@ -3918,6 +3919,17 @@ class TestGitRepositoryWebservice(TestCaseWithFactory):
37 self.assertEqual(200, response.status)
38 self.assertIsNone(response.jsonBody())
39
40+ def test_getRefByPath_query_count(self):
41+ repository_db = self.factory.makeGitRepository()
42+ ref_dbs = self.factory.makeGitRefs(
43+ repository=repository_db, paths=[
44+ "refs/heads/devel", "refs/heads/master"])
45+
46+ with StormStatementRecorder() as recorder:
47+ ref = repository_db.getRefByPath("master")
48+ self.assertEqual(ref_dbs[1], ref)
49+ self.assertEqual(1, recorder.count)
50+
51 def test_subscribe(self):
52 # A user can subscribe to a repository.
53 repository_db = self.factory.makeGitRepository()