Merge lp:~jtv/launchpad/bug-618393 into lp:launchpad
| Status: | Merged |
|---|---|
| Approved by: | Jeroen T. Vermeulen on 2010-09-03 |
| Approved revision: | no longer in the source branch. |
| Merged at revision: | 11498 |
| Proposed branch: | lp:~jtv/launchpad/bug-618393 |
| Merge into: | lp:launchpad |
| Diff against target: |
804 lines (+361/-103) 10 files modified
lib/lp/registry/model/pillar.py (+7/-27) lib/lp/registry/model/product.py (+49/-5) lib/lp/registry/tests/test_productwithlicenses.py (+114/-0) lib/lp/translations/browser/tests/test_translationgroup.py (+2/-6) lib/lp/translations/browser/translationgroup.py (+4/-3) lib/lp/translations/doc/translationgroup.txt (+9/-7) lib/lp/translations/interfaces/translationgroup.py (+28/-2) lib/lp/translations/model/translationgroup.py (+114/-21) lib/lp/translations/templates/translationgroup-index.pt (+17/-16) lib/lp/translations/templates/translationgroup-portlet-projects.pt (+17/-16) |
| To merge this branch: | bzr merge lp:~jtv/launchpad/bug-618393 |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Henning Eggers (community) | code | 2010-09-03 | Approve on 2010-09-03 |
|
Review via email:
|
|||
Commit Message
TranslationGrou
Description of the Change
= Bug 618393 =
Working on some timeouts on the TranslationGroup pages.
I looked at one oops in detail. A narrow majority of the time was taken up by database queries. So I mostly attacked those before resorting to python profiling.
Here I exploit several optimization opportunities:
* Prejoin icon data for persons, persons, etc. (20% of queries).
* Prejoin project license information (15% of queries).
* Cache whether user has Edit privileges in the view.
* Query translators once; avoid redundant is_empty check.
* Query for projects/project groups/
The most interesting work is on prefetching project license information. What makes this necessary is the link formatter for Product: it queries for licenses in order to flag projects whose licenses have not yet been set.
These are not queries one can avoid through Storm caching. Luckily there is a class that lets me get around that: ProductWithLice
You'll notice that I had to change the license_status property in ProductWithLice
To test,
{{{
./bin/test -vvc lp.translations -t translationgroup
./bin/test -vvc lp.registry -t productwithlicenses -t pillar.txt
}}}
To Q/A, go to
https:/
It should not time out. Also, the page should issue at most 600 or so queries, as opposed to the current 1,000.
No lint,
Jeroen
| Henning Eggers (henninge) wrote : | # |
Hi Jeroen,
very nice branch and I learned a few new things by reviewing it. Thanks. ;-)
I could not find anything serious so please bear my nit-picks with patience.
And thank you for all the drive-by fixes! ;)
Cheers,
Henning
Am 03.09.2010 08:46, schrieb Jeroen T. Vermeulen:
> > === modified file 'lib/lp/
> > @@ -244,18 +230,12 @@
> > stacklevel=2)
> > pillars = []
> > # Prefill pillar.
> > - for pillar_name, other, product, project, distro, license_ids in (
> > + for pillar_name, other, product, project, distro, licenses in (
> > result[:limit]):
You can fix this confusing indention by pulling the de-tupling into the loop body.
> > pillar = pillar_name.pillar
> > if IProduct.
> > - licenses = [
> > - License.
> > - for license_id in license_ids]
> > - product_
> > - pillar, tuple(sorted(
> > - pillars.
> > - else:
> > - pillars.
> > + pillar = ProductWithLice
> > + pillars.
> > return pillars
> >
> > def add_featured_
> >
> > === modified file 'lib/lp/
> > @@ -201,14 +202,25 @@
> > return LicenseStatus.
> >
> >
> > +class Array(NamedFunc):
> > + """Implements the postgres "array" function in Storm."""
> > + name = 'array'
> > +
> > +
> > class ProductWithLice
> > """Caches `Product.
> >
> > delegates(IProduct, 'product')
> >
> > - def __init__(self, product, licenses):
> > + def __init__(self, product, license_ids):
> > + """Initialize a `ProductWithLic
> > +
> > + :param product: the `Product` to wrap.
> > + :param license_ids: a sequence of numeric `License` ids.
> > + """
> > self.product = product
> > - self._licenses = licenses
> > + self._licenses = tuple([
> > + License.items[id] for id in sorted(
You don't need the [] inside the tuple(). Go straight from iterator to tuple.
(I just found that out recently myself.)
> >
> > @property
> > def licenses(self):
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -186,8 +186,34 @@
> > def fetchTranslator
> > """Fetch translators and related data.
> >
> > - :return: A tuple (`Translator`, `Language`, `Person`), ordered
> > - by language name in English.
> > + Prefetches display-related properties.
> > +
> > + :return: A result set of (`Translator`, `Language`, `Person`),
> > + ordered by language name in English.
> > + """
> > +
> > + def fetchProjectsFo
I w...
| Jeroen T. Vermeulen (jtv) wrote : | # |
Thanks. Wow, you did some careful research!
All your suggestions implemented, except moving the order_by out of the prejoin. (If nothing else, I like to avoid any risk of triggering query-before-slice bugs a priori). I didn't know tuple comprehensions were in python 2.5; I thought they were 2.6-only and so didn't use them.
You're also right that I shouldn't have said "Project" for "Product" in model code. It was reckless of me, and it is now undone.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Jeroen,
very nice branch and I learned a few new things by reviewing it. Thanks. ;-)
I could not find anything serious so please bear my nit-picks with patience.
And thank you for all the drive-by fixes! ;)
review approve code
Cheers,
Henning
Am 03.09.2010 08:46, schrieb Jeroen T. Vermeulen: registry/ model/pillar. py' product. licenses.
> === modified file 'lib/lp/
> @@ -244,18 +230,12 @@
> stacklevel=2)
> pillars = []
> # Prefill pillar.
> - for pillar_name, other, product, project, distro, license_ids in (
> + for pillar_name, other, product, project, distro, licenses in (
> result[:limit]):
You can fix this confusing indention by pulling the de-tupling into the loop body.
> pillar = pillar_name.pillar providedBy( pillar) : items[license_ id] with_licenses = ProductWithLice nses( licenses) )) append( product_ with_licenses) append( pillar) nses(pillar, licenses) append( pillar) project( self, project): registry/ model/product. py' OPEN_SOURCE nses: licenses` .""" enses`. license_ ids)])
> if IProduct.
> - licenses = [
> - License.
> - for license_id in license_ids]
> - product_
> - pillar, tuple(sorted(
> - pillars.
> - else:
> - pillars.
> + pillar = ProductWithLice
> + pillars.
> return pillars
>
> def add_featured_
>
> === modified file 'lib/lp/
> @@ -201,14 +202,25 @@
> return LicenseStatus.
>
>
> +class Array(NamedFunc):
> + """Implements the postgres "array" function in Storm."""
> + name = 'array'
> +
> +
> class ProductWithLice
> """Caches `Product.
>
> delegates(IProduct, 'product')
>
> - def __init__(self, product, licenses):
> + def __init__(self, product, license_ids):
> + """Initialize a `ProductWithLic
> +
> + :param product: the `Product` to wrap.
> + :param license_ids: a sequence of numeric `License` ids.
> + """
> self.product = product
> - self._licenses = licenses
> + self._licenses = tuple([
> + License.items[id] for id in sorted(
You don't need the [] inside the tuple(). Go straight from iterator to tuple.
(I just found that out recently myself.)
>
> @property
> def licenses(self):
> === modified file 'lib/lp/ translations/ interfaces/ translationgrou p.py' translations/ interfaces/ translationgrou p.py 2010-08-20 20:31:18 +0000 translations/ interfaces/ translationgrou p.py 2010-09-03 06:46:46 +0000 Data(): rDisplay( ):
> --- lib/lp/
> +++ lib/lp/
> @@ -186,8 +186,34 @@
> def fetchTranslator
> """Fetch translators and related data.
>
> - :return: A tuple (`Translator`, `Language`, `Person`), ordered
> - by language name in English.
> + Prefetches display-related properties.
> +
> + :return: A result set of (`Translator`, `Language`, `Person`),
> + ordered by language name in English.
> + """
> +
> + def fetchProjectsFo
I would ask you to use "fetchProductsF orDisplay" as l...