Merge lp:~thumper/launchpad/simplistic-official-links-speedup into lp:launchpad

Proposed by Tim Penhey
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~thumper/launchpad/simplistic-official-links-speedup
Merge into: lp:launchpad
Diff against target: 242 lines
4 files modified
lib/lp/code/browser/branchlisting.py (+65/-23)
lib/lp/code/browser/tests/test_branchlisting.py (+23/-0)
lib/lp/code/interfaces/branch.py (+5/-5)
lib/lp/code/model/branch.py (+6/-2)
To merge this branch: bzr merge lp:~thumper/launchpad/simplistic-official-links-speedup
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle Approve
Review via email: mp+13681@code.launchpad.net

Commit message

Be more efficient generating the bazaar identities for the branch listings.

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

= Summary =

This branch reduces the query count on branch listings that contain package
branches.

== Proposed fix ==

Get the branch listing browser classes to use the query optimiser to load all
the suite source package links for the branches, and make those available
through the decorated Branch class.

Move the package development focus check into the model code for branch, and
change the bazaar_identity function to us is_dev_focus for package branches.

Also determine the development focus in the browser class.

== Pre-implementation notes ==

Distribution.currentseries is an abomination and had to be worked around in
the browser class.

== Implementation details ==

I've deferred some possible refactoring of the dev focus checks.

== Tests ==

test_linked_to_development_package
TestDevelopmentFocusPackageBranches
code/stories
TestBzrIdentity

== Demo and Q/A ==

Add the mint distro using the code test helper, and go to look at:
https://code.launchpad.dev/mint/+branches?batch=150

and look for lp://dev/mint/twisted

 reviewer mwhudson

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :
Download full text (10.1 KiB)

Generally, I like this branch. It makes the handling of product and
package more consistent, which has to be a good thing. And it reduces
the query counts!

Did you think about adding tests that check how many queries we're issuing?

> === modified file 'lib/lp/code/browser/branchlisting.py'
> --- lib/lp/code/browser/branchlisting.py 2009-09-22 18:45:02 +0000
> +++ lib/lp/code/browser/branchlisting.py 2009-10-21 02:06:48 +0000
> @@ -87,6 +87,7 @@
> ProductDownloadFileMixin, SortSeriesMixin)
> from lp.registry.interfaces.distroseries import DistroSeriesStatus
> from lp.registry.interfaces.person import IPerson, IPersonSet
> +from lp.registry.interfaces.pocket import PackagePublishingPocket
> from lp.registry.interfaces.product import IProduct
> from lp.registry.interfaces.sourcepackage import ISourcePackageFactory
> from lp.registry.model.sourcepackage import SourcePackage
> @@ -153,21 +154,26 @@
> delegates(IBranch, 'context')
>
> def __init__(self, branch, last_commit, now, show_bug_badge,
> - show_blueprint_badge, is_dev_focus,
> - associated_product_series, show_mp_badge):
> + show_blueprint_badge, show_mp_badge,
> + associated_product_series, suite_source_packages, is_dev_focus):
> BranchBadges.__init__(self, branch)
> self.last_commit = last_commit
> self.show_bug_badge = show_bug_badge
> self.show_blueprint_badge = show_blueprint_badge
> self.show_merge_proposals = show_mp_badge
> self._now = now
> + self.associated_product_series = associated_product_series
> + self.suite_source_packages = suite_source_packages
> self.is_development_focus = is_dev_focus
> - self.associated_product_series = associated_product_series
>
> def associatedProductSeries(self):
> """Override the IBranch.associatedProductSeries."""
> return self.associated_product_series
>
> + def associatedSuiteSourcePackages(self):
> + """Override the IBranch.associatedSuiteSourcePackages."""
> + return self.suite_source_packages
> +
> @property
> def active_series(self):
> return [series for series in self.associated_product_series
> @@ -314,7 +320,7 @@
> # Requires the following attributes:
> # visible_branches_for_view
> def __init__(self, user):
> - self._dev_series_map = {}
> + self._distro_series_map = {}
> self._now = datetime.now(pytz.UTC)
> self.view_user = user
>
> @@ -373,21 +379,63 @@
> series.insert(0, dev_focus)
> return series
>
> - def getDevFocusBranch(self, branch):
> - """Get the development focus branch that relates to `branch`."""
> - # XXX: 2009-07-02 TimPenhey spec=package-branches We need to determine
> - # here what constitutes a dev focus branch for package branches, and
> - # that perhaps this should also refer to targets instead of using
> - # product.
> - if branch.product is None:
> - return None
> + @cachedproperty
> + def official_package_links_map(self):
> + """Return a map of ...

review: Approve
Revision history for this message
Tim Penhey (thumper) wrote :

On Wed, 21 Oct 2009 16:22:06 Michael Hudson wrote:
> Review: Approve
> Generally, I like this branch. It makes the handling of product and
> package more consistent, which has to be a good thing. And it reduces
> the query counts!
>
> Did you think about adding tests that check how many queries we're issuing?

I brought this up at the team lead sprints, and it was somewhat discouraged.

Explicitly checking at some levels about the exact number of queries being
used is ok, but not checking less than a certain level.

Probably better to graph and track query counts on key pages.

> > === modified file 'lib/lp/code/browser/branchlisting.py'
> > --- lib/lp/code/browser/branchlisting.py 2009-09-22 18:45:02 +0000
> > +++ lib/lp/code/browser/branchlisting.py 2009-10-21 02:06:48 +0000
> > + @cachedproperty
> > + def official_package_links_map(self):
> > + """Return a map of branch id to a list of package links.
>
> I guess it's a bit hard, English being English, but I found this hard
> to grok. s/of/from/ would help a bit, I think.

Fair enough, changed.

> > + While this code is still valid with package branches is it a
> > query + that isn't needed.
> > + """
>
> Here though I think you have a confusing thinko: s/package/product/
> surely? I'm not quite sure what you mean at a higher level -- are you
> saying that this will be a waste on a page that we know only has
> product branches? In which case an XXX comment would seem more
> appropiate than a mention in the docstring.
>
> Oh, I see: you copied this from product_series_map.__doc__ :-) Please
> fix up both.

Done.

> > === modified file 'lib/lp/code/model/tests/test_branch.py'
> > --- lib/lp/code/model/tests/test_branch.py 2009-10-15 05:45:57 +0000
> > +++ lib/lp/code/model/tests/test_branch.py 2009-10-21 02:06:48 +0000
> > @@ -486,6 +486,23 @@
> > login(ANONYMOUS)
> > self.assertBzrIdentity(branch, linked_branch.bzr_path)
> >
> > + def test_linked_to_development_package(self):
> > + # If a branch is linked to the RELEASE pocket of the current
> > + # distroseries for the source package, then the bzr identity is
> > the + # distribution name followed by the sourcepackage name. +
> > branch = self.factory.makePackageBranch()
> > + pocket = PackagePublishingPocket.RELEASE
> > + linked_branch = ICanHasLinkedBranch(
> > + branch.sourcepackage.getSuiteSourcePackage(pocket))
> > + registrant = getUtility(
> > + ILaunchpadCelebrities).ubuntu_branches.teamowner
> > + login_person(registrant)
> > + linked_branch.setBranch(branch, registrant)
> > + logout()
> > + login(ANONYMOUS)
> > + self.assertBzrIdentity(branch, '%s/%s' % (
> > + branch.distribution.name, branch.sourcepackagename.name))
>
> As pointed out in IRC, this test is basically the same as the following.

Yep, deleted.

> Cheers,
> mwh

thanks

Revision history for this message
Jonathan Lange (jml) wrote :

On Thu, Oct 22, 2009 at 12:57 AM, Tim Penhey <email address hidden> wrote:
> On Wed, 21 Oct 2009 16:22:06 Michael Hudson wrote:
>> Review: Approve
>> Generally, I like this branch.  It makes the handling of product and
>> package more consistent, which has to be a good thing.  And it reduces
>> the query counts!
>>
>> Did you think about adding tests that check how many queries we're issuing?
>
> I brought this up at the team lead sprints, and it was somewhat discouraged.
>

I don't recall it being discouraged. I just remember Bjorn & Martin
Pool saying to check for equality rather than less than.

jml

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/browser/branchlisting.py'
--- lib/lp/code/browser/branchlisting.py 2009-09-22 18:45:02 +0000
+++ lib/lp/code/browser/branchlisting.py 2009-10-21 23:58:14 +0000
@@ -87,6 +87,7 @@
87 ProductDownloadFileMixin, SortSeriesMixin)87 ProductDownloadFileMixin, SortSeriesMixin)
88from lp.registry.interfaces.distroseries import DistroSeriesStatus88from lp.registry.interfaces.distroseries import DistroSeriesStatus
89from lp.registry.interfaces.person import IPerson, IPersonSet89from lp.registry.interfaces.person import IPerson, IPersonSet
90from lp.registry.interfaces.pocket import PackagePublishingPocket
90from lp.registry.interfaces.product import IProduct91from lp.registry.interfaces.product import IProduct
91from lp.registry.interfaces.sourcepackage import ISourcePackageFactory92from lp.registry.interfaces.sourcepackage import ISourcePackageFactory
92from lp.registry.model.sourcepackage import SourcePackage93from lp.registry.model.sourcepackage import SourcePackage
@@ -153,21 +154,26 @@
153 delegates(IBranch, 'context')154 delegates(IBranch, 'context')
154155
155 def __init__(self, branch, last_commit, now, show_bug_badge,156 def __init__(self, branch, last_commit, now, show_bug_badge,
156 show_blueprint_badge, is_dev_focus,157 show_blueprint_badge, show_mp_badge,
157 associated_product_series, show_mp_badge):158 associated_product_series, suite_source_packages, is_dev_focus):
158 BranchBadges.__init__(self, branch)159 BranchBadges.__init__(self, branch)
159 self.last_commit = last_commit160 self.last_commit = last_commit
160 self.show_bug_badge = show_bug_badge161 self.show_bug_badge = show_bug_badge
161 self.show_blueprint_badge = show_blueprint_badge162 self.show_blueprint_badge = show_blueprint_badge
162 self.show_merge_proposals = show_mp_badge163 self.show_merge_proposals = show_mp_badge
163 self._now = now164 self._now = now
165 self.associated_product_series = associated_product_series
166 self.suite_source_packages = suite_source_packages
164 self.is_development_focus = is_dev_focus167 self.is_development_focus = is_dev_focus
165 self.associated_product_series = associated_product_series
166168
167 def associatedProductSeries(self):169 def associatedProductSeries(self):
168 """Override the IBranch.associatedProductSeries."""170 """Override the IBranch.associatedProductSeries."""
169 return self.associated_product_series171 return self.associated_product_series
170172
173 def associatedSuiteSourcePackages(self):
174 """Override the IBranch.associatedSuiteSourcePackages."""
175 return self.suite_source_packages
176
171 @property177 @property
172 def active_series(self):178 def active_series(self):
173 return [series for series in self.associated_product_series179 return [series for series in self.associated_product_series
@@ -314,7 +320,7 @@
314 # Requires the following attributes:320 # Requires the following attributes:
315 # visible_branches_for_view321 # visible_branches_for_view
316 def __init__(self, user):322 def __init__(self, user):
317 self._dev_series_map = {}323 self._distro_series_map = {}
318 self._now = datetime.now(pytz.UTC)324 self._now = datetime.now(pytz.UTC)
319 self.view_user = user325 self.view_user = user
320326
@@ -345,11 +351,7 @@
345351
346 @cachedproperty352 @cachedproperty
347 def product_series_map(self):353 def product_series_map(self):
348 """Return a map of branch id to a list of product series.354 """Return a map from branch id to a list of product series."""
349
350 While this code is still valid with package branches is it a query
351 that isn't needed.
352 """
353 series_resultset = self._query_optimiser.getProductSeriesForBranches(355 series_resultset = self._query_optimiser.getProductSeriesForBranches(
354 self._visible_branch_ids)356 self._visible_branch_ids)
355 result = {}357 result = {}
@@ -373,21 +375,59 @@
373 series.insert(0, dev_focus)375 series.insert(0, dev_focus)
374 return series376 return series
375377
376 def getDevFocusBranch(self, branch):378 @cachedproperty
377 """Get the development focus branch that relates to `branch`."""379 def official_package_links_map(self):
378 # XXX: 2009-07-02 TimPenhey spec=package-branches We need to determine380 """Return a map from branch id to a list of package links."""
379 # here what constitutes a dev focus branch for package branches, and381 links = self._query_optimiser.getOfficialSourcePackageLinksForBranches(
380 # that perhaps this should also refer to targets instead of using382 self._visible_branch_ids)
381 # product.383 result = {}
382 if branch.product is None:384 for link in links:
383 return None385 result.setdefault(link.branch.id, []).append(link)
386 return result
387
388 def getSuiteSourcePackages(self, branch):
389 """Get the associated SuiteSourcePackages for the branch.
390
391 If there is more than one, they are sorted by pocket.
392 """
393 links = [link.suite_sourcepackage for link in
394 self.official_package_links_map.get(branch.id, [])]
395 return sorted(links, key=attrgetter('pocket'))
396
397 def getDistroDevelSeries(self, distribution):
398 """Since distribution.currentseries hits the DB every time, cache it."""
399 self._distro_series_map = {}
384 try:400 try:
385 return self._dev_series_map[branch.product]401 return self._distro_series_map[distribution]
386 except KeyError:402 except KeyError:
387 result = branch.product.development_focus.branch403 result = distribution.currentseries
388 self._dev_series_map[branch.product] = result404 self._distro_series_map[distribution] = result
389 return result405 return result
390406
407 def isBranchDevFocus(self, branch,
408 associated_product_series, suite_source_packages):
409 """Is the branch the development focus?
410
411 For product branches this means that the branch is linked to the
412 development focus series.
413
414 For package branches this means that the branch is linked to the
415 release pocket of the development series.
416 """
417 # Refactor this code to work for model.branch too?
418 # Do we care if a non-product branch is linked to the product series?
419 # Do we care if a non-package branch is linked to the package?
420 # A) not right now.
421 for series in associated_product_series:
422 if series.product.development_focus == series:
423 return True
424 for ssp in suite_source_packages:
425 if (ssp.pocket == PackagePublishingPocket.RELEASE and
426 ssp.distroseries == self.getDistroDevelSeries(
427 ssp.distribution)):
428 return True
429 return False
430
391 @cachedproperty431 @cachedproperty
392 def branch_ids_with_bug_links(self):432 def branch_ids_with_bug_links(self):
393 """Return a set of branch ids that should show bug badges."""433 """Return a set of branch ids that should show bug badges."""
@@ -436,11 +476,13 @@
436 show_blueprint_badge = branch.id in self.branch_ids_with_spec_links476 show_blueprint_badge = branch.id in self.branch_ids_with_spec_links
437 show_mp_badge = branch.id in self.branch_ids_with_merge_proposals477 show_mp_badge = branch.id in self.branch_ids_with_merge_proposals
438 associated_product_series = self.getProductSeries(branch)478 associated_product_series = self.getProductSeries(branch)
439 is_dev_focus = (self.getDevFocusBranch(branch) == branch)479 suite_source_packages = self.getSuiteSourcePackages(branch)
480 is_dev_focus = self.isBranchDevFocus(
481 branch, associated_product_series, suite_source_packages)
440 return BranchListingItem(482 return BranchListingItem(
441 branch, last_commit, self._now, show_bug_badge,483 branch, last_commit, self._now, show_bug_badge,
442 show_blueprint_badge, is_dev_focus,484 show_blueprint_badge, show_mp_badge,
443 associated_product_series, show_mp_badge)485 associated_product_series, suite_source_packages, is_dev_focus)
444486
445 def decoratedBranches(self, branches):487 def decoratedBranches(self, branches):
446 """Return the decorated branches for the branches passed in."""488 """Return the decorated branches for the branches passed in."""
447489
=== modified file 'lib/lp/code/browser/tests/test_branchlisting.py'
--- lib/lp/code/browser/tests/test_branchlisting.py 2009-10-01 11:21:31 +0000
+++ lib/lp/code/browser/tests/test_branchlisting.py 2009-10-21 23:58:14 +0000
@@ -307,6 +307,29 @@
307 self.assertGroupBranchesEqual(expected, series)307 self.assertGroupBranchesEqual(expected, series)
308308
309309
310class TestDevelopmentFocusPackageBranches(TestCaseWithFactory):
311 """Make sure that the bzr_identity of the branches are correct."""
312
313 layer = DatabaseFunctionalLayer
314
315 def test_package_development_focus(self):
316 # Check the bzr_identity of a development focus package branch.
317 branch = self.factory.makePackageBranch()
318 series_set = removeSecurityProxy(getUtility(IMakeOfficialBranchLinks))
319 sspb = series_set.new(
320 branch.distroseries, PackagePublishingPocket.RELEASE,
321 branch.sourcepackagename, branch, branch.owner)
322 identity = "lp://dev/%s/%s" % (
323 branch.distribution.name, branch.sourcepackagename.name)
324 self.assertEqual(identity, branch.bzr_identity)
325 # Now confirm that we get the same through the view.
326 view = create_initialized_view(branch.distribution, name='+branches')
327 # There is only one branch.
328 batch = view.branches()
329 [view_branch] = batch.branches
330 self.assertEqual(identity, view_branch.bzr_identity)
331
332
310def test_suite():333def test_suite():
311 return unittest.TestLoader().loadTestsFromName(__name__)334 return unittest.TestLoader().loadTestsFromName(__name__)
312335
313336
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2009-10-15 05:45:57 +0000
+++ lib/lp/code/interfaces/branch.py 2009-10-21 23:58:14 +0000
@@ -68,7 +68,6 @@
68from lp.code.interfaces.branchlookup import IBranchLookup68from lp.code.interfaces.branchlookup import IBranchLookup
69from lp.code.interfaces.branchtarget import IHasBranchTarget69from lp.code.interfaces.branchtarget import IHasBranchTarget
70from lp.code.interfaces.hasbranches import IHasMergeProposals70from lp.code.interfaces.hasbranches import IHasMergeProposals
71from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
72from canonical.launchpad.interfaces.launchpad import (71from canonical.launchpad.interfaces.launchpad import (
73 ILaunchpadCelebrities, IPrivacy)72 ILaunchpadCelebrities, IPrivacy)
74from lp.registry.interfaces.role import IHasOwner73from lp.registry.interfaces.role import IHasOwner
@@ -1230,10 +1229,11 @@
1230 'series': use_series.name}1229 'series': use_series.name}
12311230
1232 if branch.sourcepackage is not None:1231 if branch.sourcepackage is not None:
1233 distro_package = branch.sourcepackage.distribution_sourcepackage1232 if is_dev_focus:
1234 linked_branch = ICanHasLinkedBranch(distro_package)1233 return "%(prefix)s%(distro)s/%(packagename)s" % {
1235 if linked_branch.branch == branch:1234 'prefix': lp_prefix,
1236 return lp_prefix + linked_branch.bzr_path1235 'distro': branch.distroseries.distribution.name,
1236 'packagename': branch.sourcepackagename.name}
1237 suite_sourcepackages = branch.associatedSuiteSourcePackages()1237 suite_sourcepackages = branch.associatedSuiteSourcePackages()
1238 # Take the first link if there is one.1238 # Take the first link if there is one.
1239 if len(suite_sourcepackages) > 0:1239 if len(suite_sourcepackages) > 0:
12401240
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2009-10-06 16:24:20 +0000
+++ lib/lp/code/model/branch.py 2009-10-21 23:58:14 +0000
@@ -68,6 +68,7 @@
68from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy68from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy
69from lp.code.interfaces.branchpuller import IBranchPuller69from lp.code.interfaces.branchpuller import IBranchPuller
70from lp.code.interfaces.branchtarget import IBranchTarget70from lp.code.interfaces.branchtarget import IBranchTarget
71from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
71from lp.code.interfaces.seriessourcepackagebranch import (72from lp.code.interfaces.seriessourcepackagebranch import (
72 IFindOfficialBranchLinks)73 IFindOfficialBranchLinks)
73from lp.registry.interfaces.person import (74from lp.registry.interfaces.person import (
@@ -441,11 +442,14 @@
441 @property442 @property
442 def bzr_identity(self):443 def bzr_identity(self):
443 """See `IBranch`."""444 """See `IBranch`."""
444 # XXX: JonathanLange 2009-03-19 spec=package-branches bug=345740: This445 # Should probably put this into the branch target.
445 # should not dispatch on product is None.
446 if self.product is not None:446 if self.product is not None:
447 series_branch = self.product.development_focus.branch447 series_branch = self.product.development_focus.branch
448 is_dev_focus = (series_branch == self)448 is_dev_focus = (series_branch == self)
449 elif self.distroseries is not None:
450 distro_package = self.sourcepackage.distribution_sourcepackage
451 linked_branch = ICanHasLinkedBranch(distro_package)
452 is_dev_focus = (linked_branch.branch == self)
449 else:453 else:
450 is_dev_focus = False454 is_dev_focus = False
451 return bazaar_identity(self, is_dev_focus)455 return bazaar_identity(self, is_dev_focus)