Merge lp:~cjwatson/launchpad/packageset-score-rename-support into lp:launchpad

Proposed by Colin Watson on 2012-05-30
Status: Merged
Approved by: j.c.sackett on 2012-05-31
Approved revision: no longer in the source branch.
Merged at revision: 15343
Proposed branch: lp:~cjwatson/launchpad/packageset-score-rename-support
Merge into: lp:launchpad
Diff against target: 95 lines (+50/-3)
2 files modified
lib/lp/soyuz/model/buildpackagejob.py (+1/-2)
lib/lp/soyuz/model/packageset.py (+49/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/packageset-score-rename-support
Reviewer Review Type Date Requested Status
j.c.sackett (community) 2012-05-30 Approve on 2012-05-31
Review via email: mp+107981@code.launchpad.net

Commit Message

Prepare for renaming Packageset.score DB column to Packageset.relative_build_score.

Description of the Change

== Summary ==

Bug 1000570 reports that Packageset.score is misnamed. I renamed the Python attribute a while back, but this branch prepares for renaming the database column as well.

== Proposed fix ==

Unashamedly plagiarise https://code.launchpad.net/~jml/launchpad/archive-commercial-rename-support/+merge/107819, with the same rationale.

== Implementation details ==

Unlike Archive.commercial, there was one instance of Packageset.score being used a query (indirectly via a Storm aggregate of Packageset.relative_build_score). The tests caught this and I temporarily switched from package_sets.max(Packageset.relative_build_score) to max(ps.relative_build_score for ps in package_sets). This will be slightly less efficient, but it's very unlikely to be a performance hot-spot, so it won't be a problem to have this in place for a few days.

== LOC Rationale ==

+47, but I'll revert all of that after the corresponding database patch has been deployed.

== Tests ==

bin/test -vvct TestBuildPackageJobScore -t TestBuildQueueManual

== Lint ==

./lib/lp/soyuz/model/packageset.py
     114: redefinition of function 'relative_build_score' from line 105

False positive due to pocketlint not understanding setter properties.

To post a comment you must log in.
j.c.sackett (jcsackett) wrote :

This looks ok to land.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
2--- lib/lp/soyuz/model/buildpackagejob.py 2012-05-17 16:24:42 +0000
3+++ lib/lp/soyuz/model/buildpackagejob.py 2012-05-30 14:08:24 +0000
4@@ -40,7 +40,6 @@
5 )
6 from lp.soyuz.interfaces.packageset import IPackagesetSet
7 from lp.soyuz.model.buildfarmbuildjob import BuildFarmBuildJob
8-from lp.soyuz.model.packageset import Packageset
9
10
11 class BuildPackageJob(BuildFarmJobOldDerived, Storm):
12@@ -111,7 +110,7 @@
13 self.build.source_package_release.name,
14 distroseries=self.build.distro_series)
15 if not package_sets.is_empty():
16- score += package_sets.max(Packageset.relative_build_score)
17+ score += max(ps.relative_build_score for ps in package_sets)
18
19 # Calculates the build queue time component of the score.
20 right_now = datetime.now(pytz.timezone('UTC'))
21
22=== modified file 'lib/lp/soyuz/model/packageset.py'
23--- lib/lp/soyuz/model/packageset.py 2012-05-17 16:24:42 +0000
24+++ lib/lp/soyuz/model/packageset.py 2012-05-30 14:08:24 +0000
25@@ -14,6 +14,7 @@
26 Storm,
27 Unicode,
28 )
29+from storm.store import Store
30 from zope.component import getUtility
31 from zope.interface import implements
32
33@@ -28,6 +29,8 @@
34 IMasterStore,
35 IStore,
36 )
37+from lp.services.database.postgresql import table_has_column
38+from lp.services.database.sqlbase import cursor
39 from lp.services.helpers import ensure_unicode
40 from lp.soyuz.interfaces.packageset import (
41 DuplicatePackagesetName,
42@@ -70,7 +73,52 @@
43 packagesetgroup_id = Int(name='packagesetgroup', allow_none=False)
44 packagesetgroup = Reference(packagesetgroup_id, 'PackagesetGroup.id')
45
46- relative_build_score = Int(name="score", allow_none=False)
47+ # Provide a manual property instead of declaring the column in Storm.
48+ # This is to handle a transition period where we rename the 'score'
49+ # column to 'relative_build_score'. During the transition period, the
50+ # code needs to work with both column names.
51+ #
52+ # The approach taken here only works because we never use 'score' in a
53+ # WHERE clause or anything like that.
54+ #
55+ # Once the database change has taken place, this property should be
56+ # deleted, and replaced with a class variable declaration as follows:
57+ #
58+ # relative_build_score = Int(allow_none=False)
59+
60+ def _get_relative_build_score_column_name(self):
61+ """Get the name of the column containing a relative build score.
62+
63+ Older versions of the database call it 'score'; newer ones call it
64+ 'relative_build_score'.
65+
66+ Works by interrogating PostgreSQL's own records.
67+ """
68+ # Chose this look-before-you-leap implementation so as to avoid
69+ # invalidating the query by forcing a ProgrammingError.
70+ cur = cursor()
71+ if table_has_column(cur, "packageset", "score"):
72+ return "score"
73+ else:
74+ return "relative_build_score"
75+
76+ @property
77+ def relative_build_score(self):
78+ """See `IPackageset`."""
79+ store = Store.of(self)
80+ store.flush()
81+ column = self._get_relative_build_score_column_name()
82+ query = "SELECT %s FROM packageset WHERE id=%%s" % column
83+ return store.execute(query, (self.id,)).get_one()[0]
84+
85+ @relative_build_score.setter
86+ def relative_build_score(self, score):
87+ """See `IPackageset`."""
88+ store = Store.of(self)
89+ store.flush()
90+ column = self._get_relative_build_score_column_name()
91+ query = "UPDATE packageset SET %s=%%s WHERE id=%%s" % column
92+ return store.execute(query, (int(score), self.id))
93
94 def add(self, data):
95 """See `IPackageset`."""