launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #20942
[Merge] lp:~cjwatson/launchpad/getMergeProposals-timeout into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/getMergeProposals-timeout into lp:launchpad.
Commit message:
Make Person.getMergeProposals have a constant query count on the webservice.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1619772 in Launchpad itself: "user.getMergeProposals(status="Merged") consistently timing out for some users"
https://bugs.launchpad.net/launchpad/+bug/1619772
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/getMergeProposals-timeout/+merge/305065
Make Person.getMergeProposals have a constant query count on the webservice.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/getMergeProposals-timeout into lp:launchpad.
=== modified file 'lib/lp/code/interfaces/hasbranches.py'
--- lib/lp/code/interfaces/hasbranches.py 2015-05-12 17:30:40 +0000
+++ lib/lp/code/interfaces/hasbranches.py 2016-09-07 03:14:52 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Interface definitions for IHas<code related bits>."""
@@ -87,15 +87,17 @@
status=List(
title=_("A list of merge proposal statuses to filter by."),
value_type=Choice(vocabulary=BranchMergeProposalStatus)))
- @call_with(visible_by_user=REQUEST_USER)
+ @call_with(visible_by_user=REQUEST_USER, eager_load=True)
@operation_returns_collection_of(Interface) # Really IBranchMergeProposal.
@export_read_operation()
@operation_for_version('beta')
- def getMergeProposals(status=None, visible_by_user=None):
+ def getMergeProposals(status=None, visible_by_user=None, eager_load=False):
"""Returns all merge proposals of a given status.
:param status: A list of statuses to filter with.
:param visible_by_user: Normally the user who is asking.
+ :param eager_load: If True, load related objects for the whole
+ collection.
:returns: A list of `IBranchMergeProposal`.
"""
=== modified file 'lib/lp/code/model/branchmergeproposal.py'
--- lib/lp/code/model/branchmergeproposal.py 2016-09-02 18:21:07 +0000
+++ lib/lp/code/model/branchmergeproposal.py 2016-09-07 03:14:52 +0000
@@ -101,6 +101,7 @@
from lp.services.config import config
from lp.services.database.bulk import (
load,
+ load_referencing,
load_related,
)
from lp.services.database.constants import (
@@ -521,7 +522,11 @@
dbName='superseded_by', foreignKey='BranchMergeProposal',
notNull=False, default=None)
- supersedes = Reference("<primary key>", "superseded_by", on_remote=True)
+ _supersedes = Reference("<primary key>", "superseded_by", on_remote=True)
+
+ @cachedproperty
+ def supersedes(self):
+ return self._supersedes
date_created = UtcDateTimeCol(notNull=True, default=DEFAULT)
date_review_requested = UtcDateTimeCol(notNull=False, default=None)
@@ -1288,9 +1293,21 @@
Desc(PreviewDiff.date_created)).config(
distinct=[PreviewDiff.branch_merge_proposal_id])
load_related(Diff, preview_diffs, ['diff_id'])
+ preview_diff_map = {}
for previewdiff in preview_diffs:
- cache = get_property_cache(previewdiff.branch_merge_proposal)
- cache.preview_diff = previewdiff
+ preview_diff_map[previewdiff.branch_merge_proposal_id] = (
+ previewdiff)
+ for mp in branch_merge_proposals:
+ get_property_cache(mp).preview_diff = preview_diff_map.get(mp.id)
+
+ # Preload other merge proposals that supersede these.
+ supersedes_map = {}
+ for other_mp in load_referencing(
+ BranchMergeProposal, branch_merge_proposals,
+ ['superseded_byID']):
+ supersedes_map[other_mp.superseded_byID] = other_mp
+ for mp in branch_merge_proposals:
+ get_property_cache(mp).supersedes = supersedes_map.get(mp.id)
# Add source branch/repository owners' to the list of pre-loaded
# persons. We need the target repository owner as well; unlike
=== modified file 'lib/lp/code/model/tests/test_hasbranches.py'
--- lib/lp/code/model/tests/test_hasbranches.py 2012-01-01 02:58:52 +0000
+++ lib/lp/code/model/tests/test_hasbranches.py 2016-09-07 03:14:52 +0000
@@ -1,13 +1,24 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Tests for classes that implement IHasBranches."""
__metaclass__ = type
+from functools import partial
+
from lp.code.interfaces.hasbranches import IHasBranches
-from lp.testing import TestCaseWithFactory
+from lp.services.webapp.interfaces import OAuthPermission
+from lp.testing import (
+ api_url,
+ login_person,
+ logout,
+ record_two_runs,
+ TestCaseWithFactory,
+ )
from lp.testing.layers import DatabaseFunctionalLayer
+from lp.testing.matchers import HasQueryCount
+from lp.testing.pages import webservice_for_person
class TestIHasBranches(TestCaseWithFactory):
@@ -29,3 +40,37 @@
# ProjectGroups should implement IHasBranches.
project = self.factory.makeProject()
self.assertProvides(project, IHasBranches)
+
+
+class TestHasMergeProposalsWebservice(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_constant_query_count(self):
+ # Person.getMergeProposals has a constant query count on the
+ # webservice.
+
+ # Ensure that both runs fit in a single batch, to avoid needing to
+ # account for an extra count query.
+ self.pushConfig("launchpad", default_batch_size=10)
+
+ owner = self.factory.makePerson()
+ owner_url = api_url(owner)
+ webservice = webservice_for_person(
+ owner, permission=OAuthPermission.READ_PUBLIC)
+
+ def create_merge_proposal():
+ source_branch = self.factory.makeProductBranch(owner=owner)
+ self.factory.makeBranchMergeProposal(source_branch=source_branch)
+ [source_ref] = self.factory.makeGitRefs(owner=owner)
+ self.factory.makeBranchMergeProposalForGit(source_ref=source_ref)
+
+ def get_merge_proposals():
+ logout()
+ response = webservice.named_get(owner_url, "getMergeProposals")
+ self.assertEqual(200, response.status)
+
+ recorder1, recorder2 = record_two_runs(
+ get_merge_proposals, create_merge_proposal, 2,
+ login_method=partial(login_person, owner), record_request=True)
+ self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
Follow ups