Merge lp:~wgrant/launchpad/faster-checkCanBeDeleted into lp:launchpad

Proposed by William Grant on 2012-07-24
Status: Merged
Approved by: William Grant on 2012-07-24
Approved revision: no longer in the source branch.
Merged at revision: 15676
Proposed branch: lp:~wgrant/launchpad/faster-checkCanBeDeleted
Merge into: lp:launchpad
Diff against target: 73 lines (+19/-5)
2 files modified
lib/lp/bugs/model/bugtask.py (+3/-4)
lib/lp/bugs/model/tests/test_bugtask.py (+16/-1)
To merge this branch: bzr merge lp:~wgrant/launchpad/faster-checkCanBeDeleted
Reviewer Review Type Date Requested Status
Steve Kowalik (community) code 2012-07-24 Approve on 2012-07-24
Review via email: mp+116404@code.launchpad.net

Commit Message

Fix BugTask.canBeDeleted to be free if Bug.bugtasks has already been accessed. Fixes O(tasks) queries on BugTask:+index.

Description of the Change

This branch fixes bug #1028256 by making BugTask.canBeDeleted work off the bug's cached list of tasks, rather than calculating a fresh count. This fixes the leading cause of O(tasks) queries on BugTask:+index.

To post a comment you must log in.
Steve Kowalik (stevenk) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/model/bugtask.py'
2--- lib/lp/bugs/model/bugtask.py 2012-07-17 06:34:59 +0000
3+++ lib/lp/bugs/model/bugtask.py 2012-07-24 05:30:27 +0000
4@@ -622,10 +622,9 @@
5 return True
6
7 def checkCanBeDeleted(self):
8- num_bugtasks = Store.of(self).find(
9- BugTask, bug=self.bug).count()
10-
11- if num_bugtasks < 2:
12+ # Bug.bugtasks is a cachedproperty, so this is pretty much free
13+ # to call. Better than a manual count query, at any rate.
14+ if len(self.bug.bugtasks) < 2:
15 raise CannotDeleteBugtask(
16 "Cannot delete only bugtask affecting: %s."
17 % self.target.bugtargetdisplayname)
18
19=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
20--- lib/lp/bugs/model/tests/test_bugtask.py 2012-07-24 04:04:07 +0000
21+++ lib/lp/bugs/model/tests/test_bugtask.py 2012-07-24 05:30:27 +0000
22@@ -13,6 +13,7 @@
23 from lazr.lifecycle.snapshot import Snapshot
24 from lazr.restfulclient.errors import Unauthorized
25 from storm.store import Store
26+from testtools.matchers import Equals
27 from testtools.testcase import ExpectedException
28 import transaction
29 from zope.component import getUtility
30@@ -97,6 +98,7 @@
31 logout,
32 person_logged_in,
33 set_feature_flag,
34+ StormStatementRecorder,
35 TestCase,
36 TestCaseWithFactory,
37 ws_object,
38@@ -108,6 +110,7 @@
39 CeleryJobLayer,
40 DatabaseFunctionalLayer,
41 )
42+from lp.testing.matchers import HasQueryCount
43
44
45 BugData = namedtuple("BugData", ['owner', 'distro', 'distro_release',
46@@ -1476,7 +1479,7 @@
47 bug = self.factory.makeBug()
48 login_celebrity('admin')
49 self.assertTrue(
50- check_permission('launchpad.Admin', bug.default_bugtask))
51+ check_permission('launchpad.Delete', bug.default_bugtask))
52
53 def test_pillar_owner_can_delete(self):
54 # With the feature flag on, the pillar owner can delete a bug task.
55@@ -1508,6 +1511,18 @@
56 login_person(bugtask.owner)
57 self.assertRaises(CannotDeleteBugtask, bugtask.delete)
58
59+ def test_canBeDeleted_is_free(self):
60+ # BugTask.canBeDeleted uses cached data, so repeated execution
61+ # on a single bug is free.
62+ bug = self.factory.makeBug()
63+ task1 = self.factory.makeBugTask(bug=bug)
64+ task2 = self.factory.makeBugTask(bug=bug)
65+ self.assertEqual(True, bug.default_bugtask.canBeDeleted())
66+ with StormStatementRecorder() as recorder:
67+ self.assertEqual(True, task1.canBeDeleted())
68+ self.assertEqual(True, task2.canBeDeleted())
69+ self.assertThat(recorder, HasQueryCount(Equals(0)))
70+
71 def test_delete_bugtask(self):
72 # A bugtask can be deleted and after deletion, re-nominated.
73 owner = self.factory.makePerson()