Merge lp:~gary/launchpad/bug811447 into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 13859
Proposed branch: lp:~gary/launchpad/bug811447
Merge into: lp:launchpad
Prerequisite: lp:~gary/launchpad/bug838869
Diff against target: 181 lines (+65/-18)
2 files modified
lib/lp/bugs/model/personsubscriptioninfo.py (+15/-14)
lib/lp/bugs/model/tests/test_personsubscriptioninfo.py (+50/-4)
To merge this branch: bzr merge lp:~gary/launchpad/bug811447
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+73704@code.launchpad.net

Commit message

[r=jcsackett][bug=811447] drastically reduce the number of SQL needed for users who are team administrators and view a bug with many duplicates to which one or more of their teams is subscribed.

Description of the change

This branch fixes bug 811447 by drastically reducing the number of SQL needed for users who are team administrators and view a bug with many duplicates to which one or more of their teams is subscribed. It now takes a single query. This is done by converting the resultset of administrated teams to a list of administrated team ids, and then that list is consulted, rather than the result set.

Lint is happy.

This depends on lp:~gary/launchpad/bug838869 for a tool I used as test fixture.

Thank you.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Nice work, Gary.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/model/personsubscriptioninfo.py'
2--- lib/lp/bugs/model/personsubscriptioninfo.py 2011-05-17 12:49:03 +0000
3+++ lib/lp/bugs/model/personsubscriptioninfo.py 2011-09-01 17:23:38 +0000
4@@ -59,9 +59,9 @@
5
6 implements(IAbstractSubscriptionInfoCollection)
7
8- def __init__(self, person, administrated_teams):
9+ def __init__(self, person, administrated_team_ids):
10 self.person = person
11- self.administrated_teams = administrated_teams
12+ self.administrated_team_ids = administrated_team_ids
13 self.personal = []
14 self.as_team_member = []
15 self.as_team_admin = []
16@@ -72,7 +72,7 @@
17 collection = self.personal
18 else:
19 assert principal.isTeam(), (principal, self.person)
20- if principal in self.administrated_teams:
21+ if principal.id in self.administrated_team_ids:
22 collection = self.as_team_admin
23 else:
24 collection = self.as_team_member
25@@ -88,9 +88,9 @@
26
27 implements(IVirtualSubscriptionInfoCollection)
28
29- def __init__(self, person, administrated_teams):
30+ def __init__(self, person, administrated_team_ids):
31 super(VirtualSubscriptionInfoCollection, self).__init__(
32- person, administrated_teams)
33+ person, administrated_team_ids)
34 self._principal_pillar_to_info = {}
35
36 def _add_item_to_collection(self,
37@@ -110,9 +110,9 @@
38
39 implements(IRealSubscriptionInfoCollection)
40
41- def __init__(self, person, administrated_teams):
42+ def __init__(self, person, administrated_team_ids):
43 super(RealSubscriptionInfoCollection, self).__init__(
44- person, administrated_teams)
45+ person, administrated_team_ids)
46 self._principal_bug_to_infos = {}
47
48 def _add_item_to_collection(self, collection, principal,
49@@ -189,13 +189,13 @@
50 TeamParticipation.teamID == Person.id)
51
52 direct = RealSubscriptionInfoCollection(
53- self.person, self.administrated_teams)
54+ self.person, self.administrated_team_ids)
55 duplicates = RealSubscriptionInfoCollection(
56- self.person, self.administrated_teams)
57+ self.person, self.administrated_team_ids)
58 bugs = set()
59 for subscription, subscribed_bug, subscriber in info:
60 bugs.add(subscribed_bug)
61- if subscribed_bug != bug:
62+ if subscribed_bug.id != bug.id:
63 # This is a subscription through a duplicate.
64 collection = duplicates
65 else:
66@@ -232,7 +232,8 @@
67
68 def loadSubscriptionsFor(self, person, bug):
69 self.person = person
70- self.administrated_teams = person.getAdministratedTeams()
71+ self.administrated_team_ids = [
72+ team.id for team in person.getAdministratedTeams()]
73 self.bug = bug
74
75 # First get direct and duplicate real subscriptions.
76@@ -244,9 +245,9 @@
77
78 # Then get owner and assignee virtual subscriptions.
79 as_owner = VirtualSubscriptionInfoCollection(
80- self.person, self.administrated_teams)
81+ self.person, self.administrated_team_ids)
82 as_assignee = VirtualSubscriptionInfoCollection(
83- self.person, self.administrated_teams)
84+ self.person, self.administrated_team_ids)
85 for bugtask in bug.bugtasks:
86 pillar = self._getTaskPillar(bugtask)
87 owner = pillar.owner
88@@ -301,7 +302,7 @@
89 }
90 direct = {}
91 from_duplicate = {}
92- as_owner = {} # This is an owner of a pillar with no bug supervisor.
93+ as_owner = {} # This is an owner of a pillar with no bug supervisor.
94 as_assignee = {}
95 subscription_data = {
96 'direct': direct,
97
98=== modified file 'lib/lp/bugs/model/tests/test_personsubscriptioninfo.py'
99--- lib/lp/bugs/model/tests/test_personsubscriptioninfo.py 2011-05-17 12:49:03 +0000
100+++ lib/lp/bugs/model/tests/test_personsubscriptioninfo.py 2011-09-01 17:23:38 +0000
101@@ -5,8 +5,10 @@
102
103 __metaclass__ = type
104
105+from testtools.matchers import LessThan
106 from zope.security.proxy import removeSecurityProxy
107
108+from canonical.launchpad.webapp.adapter import SQLLogger
109 from canonical.testing import DatabaseFunctionalLayer
110 from lp.bugs.interfaces.personsubscriptioninfo import (
111 IRealSubscriptionInfo,
112@@ -33,6 +35,20 @@
113 self.bug = self.factory.makeBug()
114 self.subscriptions = PersonSubscriptions(self.subscriber, self.bug)
115
116+ def makeDuplicates(self, count=1, subscriber=None):
117+ if subscriber is None:
118+ subscriber = self.subscriber
119+ if subscriber.isTeam():
120+ subscribed_by = subscriber.teamowner
121+ else:
122+ subscribed_by = subscriber
123+ duplicates = [self.factory.makeBug() for i in range(count)]
124+ with person_logged_in(subscribed_by):
125+ for duplicate in duplicates:
126+ duplicate.markAsDuplicate(self.bug)
127+ duplicate.subscribe(subscriber, subscribed_by)
128+ return duplicates
129+
130 def assertCollectionsAreEmpty(self, except_=None):
131 names = ('direct', 'from_duplicate', 'as_owner', 'as_assignee')
132 assert except_ is None or except_ in names
133@@ -284,10 +300,7 @@
134
135 def test_duplicate_direct(self):
136 # Subscribed directly to the duplicate bug.
137- duplicate = self.factory.makeBug()
138- with person_logged_in(self.subscriber):
139- duplicate.markAsDuplicate(self.bug)
140- duplicate.subscribe(self.subscriber, self.subscriber)
141+ [duplicate] = self.makeDuplicates(count=1)
142 # Load a `PersonSubscriptionInfo`s for subscriber and a bug.
143 self.subscriptions.reload()
144
145@@ -487,3 +500,36 @@
146 self.subscriptions.reload()
147
148 self.failUnless(self.subscriptions.muted)
149+
150+ def test_many_duplicate_team_admin_subscriptions_few_queries(self):
151+ # This is related to bug 811447. The user is subscribed to a
152+ # duplicate bug through team membership in which the user is an admin.
153+ team = self.factory.makeTeam()
154+ with person_logged_in(team.teamowner):
155+ team.addMember(self.subscriber, team.teamowner,
156+ status=TeamMembershipStatus.ADMIN)
157+ self.makeDuplicates(count=1, subscriber=team)
158+ logger = SQLLogger()
159+ with logger:
160+ self.subscriptions.reload()
161+ # This should produce a very small number of queries.
162+ count_with_one_subscribed_duplicate = len(logger.queries)
163+ self.assertThat(count_with_one_subscribed_duplicate, LessThan(5))
164+ # It should have the correct result.
165+ self.assertCollectionsAreEmpty(except_='from_duplicate')
166+ self.assertCollectionContents(
167+ self.subscriptions.from_duplicate, as_team_admin=1)
168+ # If we increase the number of duplicates subscribed via the team that
169+ # the user administers...
170+ self.makeDuplicates(count=4, subscriber=team)
171+ with logger:
172+ self.subscriptions.reload()
173+ # ...then the query count should remain the same.
174+ count_with_five_subscribed_duplicates = len(logger.queries)
175+ self.assertEqual(
176+ count_with_one_subscribed_duplicate,
177+ count_with_five_subscribed_duplicates)
178+ # We should still have the correct result.
179+ self.assertCollectionsAreEmpty(except_='from_duplicate')
180+ self.assertCollectionContents(
181+ self.subscriptions.from_duplicate, as_team_admin=5)