Merge lp:~stevenk/launchpad/bugtask-activity-preload into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 15928
Proposed branch: lp:~stevenk/launchpad/bugtask-activity-preload
Merge into: lp:launchpad
Diff against target: 138 lines (+48/-2)
4 files modified
lib/lp/bugs/browser/bug.py (+8/-0)
lib/lp/bugs/browser/tests/test_bug_views.py (+34/-0)
lib/lp/bugs/interfaces/bugactivity.py (+5/-1)
lib/lp/bugs/templates/bug-activity.pt (+1/-1)
To merge this branch: bzr merge lp:~stevenk/launchpad/bugtask-activity-preload
Reviewer Review Type Date Requested Status
Ian Booth (community) Approve
Review via email: mp+123484@code.launchpad.net

Commit message

Pre-load persons for Bug:+activity.

Description of the change

Change Bug:+activity to bulk load the people from ValidPersonCache. With 10 BugActivity rows, the query count drops from 13 to 5, and with 50 rows the query count drops from 53 to 5. This also required adding a personID attribute onto IBugActivity.

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

Look good.

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/browser/bug.py'
2--- lib/lp/bugs/browser/bug.py 2012-08-26 22:50:37 +0000
3+++ lib/lp/bugs/browser/bug.py 2012-09-10 06:51:58 +0000
4@@ -103,6 +103,7 @@
5 get_structural_subscriptions_for_bug,
6 )
7 from lp.registry.enums import PRIVATE_INFORMATION_TYPES
8+from lp.registry.interfaces.person import IPersonSet
9 from lp.registry.vocabularies import InformationTypeVocabulary
10 from lp.services.fields import DuplicateBug
11 from lp.services.librarian.browser import ProxiedLibraryFileAlias
12@@ -605,6 +606,13 @@
13
14 page_title = 'Activity log'
15
16+ @property
17+ def activity(self):
18+ activity = IBug(self.context).activity
19+ list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
20+ [a.personID for a in activity], need_validity=True))
21+ return activity
22+
23
24 class BugSubscriptionPortletDetails:
25 """A mixin used to collate bug subscription details for a view."""
26
27=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
28--- lib/lp/bugs/browser/tests/test_bug_views.py 2012-09-06 00:01:38 +0000
29+++ lib/lp/bugs/browser/tests/test_bug_views.py 2012-09-10 06:51:58 +0000
30@@ -5,20 +5,29 @@
31
32 __metaclass__ = type
33
34+from datetime import (
35+ datetime,
36+ timedelta,
37+ )
38+
39 from BeautifulSoup import BeautifulSoup
40+import pytz
41 import simplejson
42 from soupmatchers import (
43 HTMLContains,
44 Tag,
45 )
46+from storm.store import Store
47 from testtools.matchers import (
48 Contains,
49+ Equals,
50 MatchesAll,
51 Not,
52 )
53 from zope.component import getUtility
54 from zope.security.proxy import removeSecurityProxy
55
56+from lp.bugs.adapters.bugchange import BugAttachmentChange
57 from lp.registry.enums import (
58 BugSharingPolicy,
59 InformationType,
60@@ -35,12 +44,14 @@
61 BrowserTestCase,
62 login_person,
63 person_logged_in,
64+ StormStatementRecorder,
65 TestCaseWithFactory,
66 )
67 from lp.testing.layers import (
68 DatabaseFunctionalLayer,
69 LaunchpadFunctionalLayer,
70 )
71+from lp.testing.matchers import HasQueryCount
72 from lp.testing.pages import find_tag_by_id
73 from lp.testing.views import (
74 create_initialized_view,
75@@ -673,3 +684,26 @@
76 'table',
77 {'id': 'affected-software', 'class': 'listing'})
78 self.assertIsNotNone(table)
79+
80+
81+class TestBugActivityView(TestCaseWithFactory):
82+
83+ layer = LaunchpadFunctionalLayer
84+
85+ def test_bug_activity_query_count(self):
86+ # Bug:+activity doesn't make O(n) queries based on the amount of
87+ # activity.
88+ bug = self.factory.makeBug()
89+ ten_minutes_ago = datetime.now(pytz.UTC) - timedelta(minutes=10)
90+ with person_logged_in(bug.owner):
91+ attachment = self.factory.makeBugAttachment(bug=bug)
92+ for i in range(10):
93+ bug.addChange(BugAttachmentChange(
94+ ten_minutes_ago, self.factory.makePerson(), 'attachment',
95+ None, attachment))
96+ Store.of(bug).invalidate()
97+ with StormStatementRecorder() as recorder:
98+ view = create_initialized_view(
99+ bug.default_bugtask, name='+activity')
100+ view.render()
101+ self.assertThat(recorder, HasQueryCount(Equals(7)))
102
103=== modified file 'lib/lp/bugs/interfaces/bugactivity.py'
104--- lib/lp/bugs/interfaces/bugactivity.py 2011-12-24 16:54:44 +0000
105+++ lib/lp/bugs/interfaces/bugactivity.py 2012-09-10 06:51:58 +0000
106@@ -16,7 +16,10 @@
107 export_as_webservice_entry,
108 exported,
109 )
110-from zope.interface import Interface
111+from zope.interface import (
112+ Attribute,
113+ Interface,
114+ )
115 from zope.schema import (
116 Datetime,
117 Text,
118@@ -42,6 +45,7 @@
119 description=_("The date on which this activity occurred."),
120 readonly=True))
121
122+ personID = Attribute('DB ID for Person')
123 person = exported(PersonChoice(
124 title=_('Person'), required=True, vocabulary='ValidPersonOrTeam',
125 readonly=True, description=_("The person's Launchpad ID or "
126
127=== modified file 'lib/lp/bugs/templates/bug-activity.pt'
128--- lib/lp/bugs/templates/bug-activity.pt 2010-10-10 21:54:16 +0000
129+++ lib/lp/bugs/templates/bug-activity.pt 2012-09-10 06:51:58 +0000
130@@ -20,7 +20,7 @@
131 </tr>
132 </thead>
133 <tbody>
134- <tr tal:repeat="log context/bug/activity">
135+ <tr tal:repeat="log view/activity">
136 <tal:comment condition="nothing">
137 XXX: Gavin Panella 2009-08-12 bug=412963: Using strftime()
138 here because fmt:datetime changes timezone, even though we