Merge lp:~adeuring/launchpad/bug-739065-2 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 13098
Proposed branch: lp:~adeuring/launchpad/bug-739065-2
Merge into: lp:launchpad
Prerequisite: lp:~adeuring/launchpad/bug-739065
Diff against target: 210 lines (+144/-26)
2 files modified
lib/lp/soyuz/model/distroseriessourcepackagerelease.py (+51/-26)
lib/lp/soyuz/tests/test_distroseriessourcepackagerelease.py (+93/-0)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-739065-2
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+61768@code.launchpad.net

Commit message

[r=bac][bug=739065] pre-join BinaryPackageName in the SQL query in DistroSeriesSourcePackageRelease.binaries; pre-join DistroSeriesPackageCache in the SQL query in DistributionSourcePackageRelease.sample_binary_packages

Description of the change

This branch is a second step to fix bug 739065: SourcePackage:+index timeouts.

The OOPS reports mentioned in the bug report show that two queries are repeated very often. The repetition of one of these queries is removed in the prerequiste branch ~adeuring/launchpad/bug-739065. This branch removes the other repetition.

I added some _very_ basic units for the affected property, DistroSeriesSourcePackageRelease.binaries, and I replaced the ancient SQLObject query with a Storm query.

test: ./bin/test soyuz -vvt test_distroseriessourcepackagerelease

no lint

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Abel this branch looks good. On IRC we discussed not forcing the use of the main store. Other than that it looks great. Thanks for the nice branch.

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/soyuz/model/distroseriessourcepackagerelease.py'
2--- lib/lp/soyuz/model/distroseriessourcepackagerelease.py 2011-04-18 12:53:54 +0000
3+++ lib/lp/soyuz/model/distroseriessourcepackagerelease.py 2011-05-20 14:16:58 +0000
4@@ -12,18 +12,33 @@
5 ]
6
7 from lazr.delegates import delegates
8+from operator import itemgetter
9+from storm.expr import (
10+ And,
11+ Desc,
12+ Join,
13+ )
14+from storm.store import Store
15 from zope.interface import implements
16 from zope.security.proxy import removeSecurityProxy
17
18 from canonical.database.sqlbase import sqlvalues
19+from canonical.launchpad.components.decoratedresultset import (
20+ DecoratedResultSet,
21+ )
22 from lp.registry.interfaces.distroseries import IDistroSeries
23 from lp.soyuz.enums import PackagePublishingStatus
24 from lp.soyuz.interfaces.distroseriessourcepackagerelease import (
25 IDistroSeriesSourcePackageRelease,
26 )
27 from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
28+from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
29+from lp.soyuz.model.binarypackagename import BinaryPackageName
30 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
31-from lp.soyuz.model.publishing import SourcePackagePublishingHistory
32+from lp.soyuz.model.publishing import (
33+ BinaryPackagePublishingHistory,
34+ SourcePackagePublishingHistory,
35+ )
36
37
38 class DistroSeriesSourcePackageRelease:
39@@ -112,7 +127,7 @@
40
41 return (
42 [build for build in distro_builds
43- if build.distro_arch_series.distroseries == self.distroseries])
44+ if build.distro_arch_series.distroseries == self.distroseries])
45
46 @property
47 def files(self):
48@@ -122,30 +137,40 @@
49 @property
50 def binaries(self):
51 """See `IDistroSeriesSourcePackageRelease`."""
52- clauseTables = [
53- 'BinaryPackageRelease',
54- 'DistroArchSeries',
55- 'BinaryPackageBuild',
56- 'BinaryPackagePublishingHistory'
57- ]
58-
59- query = """
60- BinaryPackageRelease.build=BinaryPackageBuild.id AND
61- DistroArchSeries.id =
62- BinaryPackagePublishingHistory.distroarchseries AND
63- BinaryPackagePublishingHistory.binarypackagerelease=
64- BinaryPackageRelease.id AND
65- DistroArchSeries.distroseries=%s AND
66- BinaryPackagePublishingHistory.archive IN %s AND
67- BinaryPackageBuild.source_package_release=%s
68- """ % sqlvalues(self.distroseries,
69- self.distroseries.distribution.all_distro_archive_ids,
70- self.sourcepackagerelease)
71-
72- return BinaryPackageRelease.select(
73- query, prejoinClauseTables=['BinaryPackageBuild'],
74- orderBy=['-id'], clauseTables=clauseTables,
75- distinct=True)
76+ # Avoid circular imports.
77+ from lp.soyuz.model.distroarchseries import DistroArchSeries
78+ store = Store.of(self.distroseries)
79+ result_row = (
80+ BinaryPackageRelease, BinaryPackageBuild, BinaryPackageName)
81+
82+ tables = (
83+ BinaryPackageRelease,
84+ Join(
85+ BinaryPackageBuild,
86+ BinaryPackageBuild.id == BinaryPackageRelease.buildID),
87+ Join(
88+ BinaryPackagePublishingHistory,
89+ BinaryPackageRelease.id ==
90+ BinaryPackagePublishingHistory.binarypackagereleaseID),
91+ Join(
92+ DistroArchSeries,
93+ DistroArchSeries.id ==
94+ BinaryPackagePublishingHistory.distroarchseriesID),
95+ Join(
96+ BinaryPackageName,
97+ BinaryPackageName.id ==
98+ BinaryPackageRelease.binarypackagenameID))
99+ archive_ids = list(
100+ self.distroseries.distribution.all_distro_archive_ids)
101+ binaries = store.using(*tables).find(
102+ result_row,
103+ And(
104+ DistroArchSeries.distroseriesID == self.distroseries.id,
105+ BinaryPackagePublishingHistory.archiveID.is_in(archive_ids),
106+ BinaryPackageBuild.source_package_release ==
107+ self.sourcepackagerelease))
108+ binaries.order_by(Desc(BinaryPackageRelease.id)).config(distinct=True)
109+ return DecoratedResultSet(binaries, itemgetter(0))
110
111 @property
112 def changesfile(self):
113
114=== added file 'lib/lp/soyuz/tests/test_distroseriessourcepackagerelease.py'
115--- lib/lp/soyuz/tests/test_distroseriessourcepackagerelease.py 1970-01-01 00:00:00 +0000
116+++ lib/lp/soyuz/tests/test_distroseriessourcepackagerelease.py 2011-05-20 14:16:58 +0000
117@@ -0,0 +1,93 @@
118+# Copyright 2011 Canonical Ltd. This software is licensed under the
119+# GNU Affero General Public License version 3 (see the file LICENSE).
120+
121+"""Tests for DistroSeriesSourcePackageRelease."""
122+
123+from storm.store import Store
124+from testtools.matchers import Equals
125+
126+from canonical.testing.layers import DatabaseFunctionalLayer
127+from lp.soyuz.enums import PackagePublishingStatus
128+from lp.soyuz.model.distroseriessourcepackagerelease import (
129+ DistroSeriesSourcePackageRelease,
130+ )
131+from lp.testing import (
132+ StormStatementRecorder,
133+ TestCaseWithFactory,
134+ )
135+from lp.testing.matchers import HasQueryCount
136+
137+
138+class TestDistroSeriesSourcePackageRelease(TestCaseWithFactory):
139+ """Tests for DistroSeriesSourcePackageRelease."""
140+
141+ layer = DatabaseFunctionalLayer
142+
143+ def setUp(self):
144+ super(TestDistroSeriesSourcePackageRelease, self).setUp()
145+ self.sourcepackagerelease = self.factory.makeSourcePackageRelease()
146+ self.distroarchseries = self.factory.makeDistroArchSeries(
147+ distroseries=self.sourcepackagerelease.sourcepackage.distroseries)
148+ self.dssp_release = DistroSeriesSourcePackageRelease(
149+ self.distroarchseries.distroseries, self.sourcepackagerelease)
150+
151+ def makeBinaryPackageRelease(self, name=None):
152+ if name is None:
153+ name = self.factory.makeBinaryPackageName()
154+ bp_build = self.factory.makeBinaryPackageBuild(
155+ source_package_release=self.sourcepackagerelease,
156+ distroarchseries=self.distroarchseries)
157+ bp_release = self.factory.makeBinaryPackageRelease(
158+ build=bp_build, binarypackagename=name)
159+ sourcepackagename = self.sourcepackagerelease.sourcepackagename
160+ self.factory.makeSourcePackagePublishingHistory(
161+ sourcepackagename,
162+ sourcepackagerelease=self.sourcepackagerelease,
163+ distroseries=self.sourcepackagerelease.sourcepackage.distroseries,
164+ status=PackagePublishingStatus.PUBLISHED)
165+ self.factory.makeBinaryPackagePublishingHistory(
166+ binarypackagerelease=bp_release,
167+ distroarchseries=self.distroarchseries)
168+ return bp_release
169+
170+ def test_binaries__no_releases(self):
171+ # If no binary releases exist,
172+ # DistroSeriesSourcePackageRelease.binaries returns an empty
173+ # sequence.
174+ self.assertEqual(0, self.dssp_release.binaries.count())
175+
176+ def test_binaries__one_release_for_source_package(self):
177+ # If a binary release exists, it is returned by
178+ # DistroSeriesSourcePackageRelease.binaries.
179+ bp_release = self.makeBinaryPackageRelease()
180+ self.assertEqual(
181+ [bp_release], list(self.dssp_release.binaries))
182+
183+ def test_binaries__two_releases_for_source_package(self):
184+ # If two binary releases with the sam name exist, both
185+ # are returned. The more recent one is returned first.
186+ name = self.factory.makeBinaryPackageName()
187+ bp_release_one = self.makeBinaryPackageRelease(name)
188+ bp_release_two = self.makeBinaryPackageRelease(name)
189+ self.assertEqual(
190+ [bp_release_two, bp_release_one],
191+ list(self.dssp_release.binaries))
192+
193+ def test_prejoins(self):
194+ # The properties BinaryPackageRelease.build and
195+ # and BinaryPackageRelease.binarypackagename of the
196+ # the result objects are preloaded in the query
197+ # issued in DistroSeriesSourcePackageRelease.binaries.
198+ self.makeBinaryPackageRelease()
199+ # Both properties we want to check have been created
200+ # in makeBinaryPackageRelease() and are thus already
201+ # in Storm's cache. We must empty the cache, otherwise
202+ # accessing bp_release.build and
203+ # bp_release.binarypackagename will never cause an
204+ # SQL query to be issued.
205+ Store.of(self.distroarchseries).invalidate()
206+ [bp_release] = list(self.dssp_release.binaries)
207+ with StormStatementRecorder() as recorder:
208+ bp_release.build
209+ bp_release.binarypackagename
210+ self.assertThat(recorder, HasQueryCount(Equals(0)))