Merge lp:~wallyworld/launchpad/bugtask-index-queries-726370 into lp:launchpad

Proposed by Ian Booth
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 16119
Proposed branch: lp:~wallyworld/launchpad/bugtask-index-queries-726370
Merge into: lp:launchpad
Diff against target: 62 lines (+37/-3)
2 files modified
lib/lp/bugs/browser/bugtask.py (+12/-3)
lib/lp/bugs/browser/tests/test_bugtask.py (+25/-0)
To merge this branch: bzr merge lp:~wallyworld/launchpad/bugtask-index-queries-726370
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+128850@code.launchpad.net

Commit message

Precache all bug activity persons to avoid individual queries on bugtask index page.

Description of the change

== Implementation ==

The bug activities rendered on the bugtask page were being iterated one by one and the activity person loaded. This caused 2 queries per activity person - one to select from Person and the other to select from ValidCache. This branches caches all activity people with a single query to prevent the linear growth in queries for this bit of the index page rendering.

== Tests ==

Add a new query count test for bugtasks with activities.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/browser/bugtask.py
  lib/lp/bugs/browser/tests/test_bugtask.py
  lib/lp/bugs/doc/bugactivity.txt
  lib/lp/bugs/doc/bugcomment.txt

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

As discussed on IRC, most of the invasive changes are unnecessary. Since it's a FK to the PK, you just need to materialise the resultset from getPrecachedPersonsFromIDs. Other than that this is great.

review: Approve (code)
Revision history for this message
Ian Booth (wallyworld) wrote :

Good pickup, thanks

On Wed 10 Oct 2012 13:12:18 EST, William Grant wrote:
> Review: Approve code
>
> As discussed on IRC, most of the invasive changes are unnecessary. Since it's a FK to the PK, you just need to materialise the resultset from getPrecachedPersonsFromIDs. Other than that this is great.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bugtask.py'
2--- lib/lp/bugs/browser/bugtask.py 2012-10-08 09:29:02 +0000
3+++ lib/lp/bugs/browser/bugtask.py 2012-10-10 03:14:21 +0000
4@@ -809,10 +809,19 @@
5 '(assignee|importance|milestone|status)')
6 interesting_match = re.compile(
7 "^(%s|%s)$" % (bug_change_re, bugtask_change_re)).match
8+
9+ activity_items = [
10+ activity_item for activity_item in activity
11+ if interesting_match(activity_item.whatchanged) is not None]
12+ # Pre-load the doers of the activities in one query.
13+ person_ids = set(
14+ activity_item.personID for activity_item in activity_items)
15+ list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
16+ person_ids, need_validity=True))
17+
18 interesting_activity = tuple(
19- BugActivityItem(activity)
20- for activity in activity
21- if interesting_match(activity.whatchanged) is not None)
22+ BugActivityItem(activity_item) for activity_item in activity_items)
23+
24 # This is a bit kludgy but it means that interesting_activity is
25 # populated correctly for all subsequent calls.
26 self._interesting_activity_cached_value = interesting_activity
27
28=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
29--- lib/lp/bugs/browser/tests/test_bugtask.py 2012-10-08 21:53:40 +0000
30+++ lib/lp/bugs/browser/tests/test_bugtask.py 2012-10-10 03:14:21 +0000
31@@ -239,6 +239,31 @@
32 [activity] = view.interesting_activity
33 self.assertEqual("description", activity.whatchanged)
34
35+ def test_rendered_query_counts_constant_with_activities(self):
36+ # More queries are not used for extra bug activities.
37+ task = self.factory.makeBugTask()
38+
39+ def add_activity(what, who):
40+ getUtility(IBugActivitySet).new(
41+ task.bug, datetime.now(UTC), who, whatchanged=what)
42+
43+ # Render the view with one activity.
44+ with celebrity_logged_in('admin'):
45+ browses_under_limit = BrowsesWithQueryLimit(
46+ 98, self.factory.makePerson())
47+ person = self.factory.makePerson()
48+ add_activity("description", person)
49+
50+ self.assertThat(task, browses_under_limit)
51+
52+ # Render the view with many more activities by different people.
53+ with celebrity_logged_in('admin'):
54+ for _ in range(20):
55+ person = self.factory.makePerson()
56+ add_activity("description", person)
57+
58+ self.assertThat(task, browses_under_limit)
59+
60 def test_error_for_changing_target_with_invalid_status(self):
61 # If a user moves a bug task with a restricted status (say,
62 # Triaged) to a target where they do not have permission to set