Merge lp:~lifeless/launchpad/bug-724033 into lp:launchpad

Proposed by Robert Collins
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 12538
Proposed branch: lp:~lifeless/launchpad/bug-724033
Merge into: lp:launchpad
Diff against target: 125 lines (+25/-14)
5 files modified
lib/canonical/launchpad/doc/vocabularies.txt (+0/-3)
lib/canonical/launchpad/vocabularies/dbobjects.py (+0/-3)
lib/canonical/launchpad/webapp/vocabulary.py (+0/-3)
lib/lp/bugs/browser/bugtask.py (+22/-5)
lib/lp/registry/vocabularies.py (+3/-0)
To merge this branch: bzr merge lp:~lifeless/launchpad/bug-724033
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) code Approve
Review via email: mp+52358@code.launchpad.net

Commit message

[r=jtv][bug=724033] More scaling improvements for BugTask:+index - use the edit form milestone cache for the view rows too.

Description of the change

More scaling improvements for BugTask:+index: this should halve the number of milestone/distro/product lookups happening by using an existing caching layer for milestones more effectively. Further work beyond this involves making the cache eager load for all contexts at once, but these are separate changes and this change alone will make a significant difference.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Strange to see the check that "return None" returns None—and good riddance I suppose.

I would still have liked to see a test, especially since the version I originally tried to review had a performance bug and the overall page is not in a shape that can be tested for query counts yet. I know it's arguably not a "new layer" and this is just a refactoring but you're trying to make an important change and you almost made things worse because of untested new code—however small.

review: Approve (code)
Revision history for this message
Robert Collins (lifeless) wrote :

Thanks for the review. I'm going to skip the test - but I'd like to note that the test you suggested would have passed even with the original bug in place.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/doc/vocabularies.txt'
--- lib/canonical/launchpad/doc/vocabularies.txt 2010-10-18 22:24:59 +0000
+++ lib/canonical/launchpad/doc/vocabularies.txt 2011-03-07 02:54:15 +0000
@@ -85,9 +85,6 @@
85 >>> debian in using_malone_vocabulary85 >>> debian in using_malone_vocabulary
86 False86 False
8787
88 >>> using_malone_vocabulary.getQuery() is None
89 True
90
91 >>> term = using_malone_vocabulary.getTerm(ubuntu)88 >>> term = using_malone_vocabulary.getTerm(ubuntu)
92 >>> print term.token, term.value.displayname, term.title89 >>> print term.token, term.value.displayname, term.title
93 ubuntu Ubuntu Ubuntu90 ubuntu Ubuntu Ubuntu
9491
=== modified file 'lib/canonical/launchpad/vocabularies/dbobjects.py'
--- lib/canonical/launchpad/vocabularies/dbobjects.py 2010-12-01 18:58:44 +0000
+++ lib/canonical/launchpad/vocabularies/dbobjects.py 2011-03-07 02:54:15 +0000
@@ -487,9 +487,6 @@
487 return (IDistribution.providedBy(obj)487 return (IDistribution.providedBy(obj)
488 and obj.bug_tracking_usage == ServiceUsage.LAUNCHPAD)488 and obj.bug_tracking_usage == ServiceUsage.LAUNCHPAD)
489489
490 def getQuery(self):
491 return None
492
493 def getTerm(self, obj):490 def getTerm(self, obj):
494 if obj not in self:491 if obj not in self:
495 raise LookupError(obj)492 raise LookupError(obj)
496493
=== modified file 'lib/canonical/launchpad/webapp/vocabulary.py'
--- lib/canonical/launchpad/webapp/vocabulary.py 2010-12-01 18:58:44 +0000
+++ lib/canonical/launchpad/webapp/vocabulary.py 2011-03-07 02:54:15 +0000
@@ -281,9 +281,6 @@
281 found_obj = self._table.selectOne(clause)281 found_obj = self._table.selectOne(clause)
282 return found_obj is not None282 return found_obj is not None
283283
284 def getQuery(self):
285 return None
286
287 def getTerm(self, value):284 def getTerm(self, value):
288 # Short circuit. There is probably a design problem here since285 # Short circuit. There is probably a design problem here since
289 # we sometimes get the id and sometimes an SQLBase instance.286 # we sometimes get the id and sometimes an SQLBase instance.
290287
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2011-03-03 10:58:03 +0000
+++ lib/lp/bugs/browser/bugtask.py 2011-03-07 02:54:15 +0000
@@ -3124,14 +3124,25 @@
31243124
3125 def __init__(self):3125 def __init__(self):
3126 self.vocabularies = {}3126 self.vocabularies = {}
3127 self.contexts = set()
31273128
3128 def __call__(self, context):3129 def __call__(self, context):
3130 assert context in self.contexts, ("context %r not added to "
3131 "self.contexts (%r)." % (context, self.contexts))
3132 self._load()
3129 target = MilestoneVocabulary.getMilestoneTarget(context)3133 target = MilestoneVocabulary.getMilestoneTarget(context)
3130 milestone_vocabulary = self.vocabularies.get(target)3134 return self.vocabularies[target]
3131 if milestone_vocabulary is None:3135
3132 milestone_vocabulary = MilestoneVocabulary(context)3136 def _load(self):
3137 """Load all the vocabularies, once only."""
3138 if self.vocabularies:
3139 return
3140 targets = set(
3141 map(MilestoneVocabulary.getMilestoneTarget, self.contexts))
3142 # TODO: instantiate for all targets at once.
3143 for target in targets:
3144 milestone_vocabulary = MilestoneVocabulary(target)
3133 self.vocabularies[target] = milestone_vocabulary3145 self.vocabularies[target] = milestone_vocabulary
3134 return milestone_vocabulary
31353146
31363147
3137class BugTasksAndNominationsView(LaunchpadView):3148class BugTasksAndNominationsView(LaunchpadView):
@@ -3205,6 +3216,7 @@
3205 The view's is_conjoined_slave and is_converted_to_question3216 The view's is_conjoined_slave and is_converted_to_question
3206 attributes are set, as well as the edit view.3217 attributes are set, as well as the edit view.
3207 """3218 """
3219 self.cached_milestone_source.contexts.add(context)
3208 view = getMultiAdapter(3220 view = getMultiAdapter(
3209 (context, self.request),3221 (context, self.request),
3210 name='+bugtasks-and-nominations-table-row')3222 name='+bugtasks-and-nominations-table-row')
@@ -3212,6 +3224,7 @@
3212 view.is_conjoined_slave = is_conjoined_slave3224 view.is_conjoined_slave = is_conjoined_slave
3213 if IBugTask.providedBy(context):3225 if IBugTask.providedBy(context):
3214 view.target_link_title = self.getTargetLinkTitle(context.target)3226 view.target_link_title = self.getTargetLinkTitle(context.target)
3227 view.milestone_source = self.cached_milestone_source
32153228
3216 view.edit_view = getMultiAdapter(3229 view.edit_view = getMultiAdapter(
3217 (context, self.request), name='+edit-form')3230 (context, self.request), name='+edit-form')
@@ -3383,6 +3396,10 @@
3383 target_link_title = None3396 target_link_title = None
3384 many_bugtasks = False3397 many_bugtasks = False
33853398
3399 def __init__(self, context, request):
3400 super(BugTaskTableRowView, self).__init__(context, request)
3401 self.milestone_source = MilestoneVocabulary
3402
3386 def canSeeTaskDetails(self):3403 def canSeeTaskDetails(self):
3387 """Whether someone can see a task's status details.3404 """Whether someone can see a task's status details.
33883405
@@ -3509,7 +3526,7 @@
3509 @cachedproperty3526 @cachedproperty
3510 def _visible_milestones(self):3527 def _visible_milestones(self):
3511 """The visible milestones for this context."""3528 """The visible milestones for this context."""
3512 return MilestoneVocabulary(self.context).visible_milestones3529 return self.milestone_source(self.context).visible_milestones
35133530
3514 @property3531 @property
3515 def milestone_widget_items(self):3532 def milestone_widget_items(self):
35163533
=== modified file 'lib/lp/registry/vocabularies.py'
--- lib/lp/registry/vocabularies.py 2011-01-31 19:44:36 +0000
+++ lib/lp/registry/vocabularies.py 2011-03-07 02:54:15 +0000
@@ -1291,6 +1291,9 @@
1291 for milestone in self.visible_milestones:1291 for milestone in self.visible_milestones:
1292 yield self.toTerm(milestone)1292 yield self.toTerm(milestone)
12931293
1294 def __len__(self):
1295 return len(self.visible_milestones)
1296
1294 def __contains__(self, obj):1297 def __contains__(self, obj):
1295 if IProjectGroupMilestone.providedBy(obj):1298 if IProjectGroupMilestone.providedBy(obj):
1296 # ProjectGroup milestones are pseudo content objects1299 # ProjectGroup milestones are pseudo content objects