Merge lp:~jtv/launchpad/bug-719267 into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 12425
Proposed branch: lp:~jtv/launchpad/bug-719267
Merge into: lp:launchpad
Diff against target: 65 lines (+25/-2)
3 files modified
lib/lp/translations/model/pofile.py (+12/-0)
lib/lp/translations/model/potemplate.py (+1/-2)
lib/lp/translations/tests/test_pofile.py (+12/-0)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-719267
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+50651@code.launchpad.net

Commit message

[r=lifeless][bug=719267] Fix timeouts on approval of new translations templates.

Description of the change

= Summary =

Timeouts were blocking translations import queue reviews, at least the creation of new templates. The time went into recalculating the statistics of each POFile that is created to match ones in sharing templates. Some large queries are involved.

== Proposed fix ==

A new template is created empty, so there's no need to query the database — all the counts that get constructed directly from the queries are zero.

I considered adding a parameter for "don't bother calculating the statistics," since the problem happens in a known scenario where the statistics don't need calculating. But in the end I just created some shortcuts: if the template is empty, the queries are skipped and zero counts are returned. This doesn't affect any APIs and may even benefit additional invocations.

== Pre-implementation notes ==

Robert looked into optimizing statistics calculation for multiple languages at once. The results are promising, though in this case there's no need to go to the trouble.

== Implementation details ==

The call that led to the unnecessary queries had an unnecessary line break. There is no other reason why I changed it.

== Tests ==

The statistics as calculated by the changed methods are extensively tested by:
{{{
./bin/test -vvc lp.translations.tests.test_pofile
}}}

I'm adding one for the zero case, just in case it's not properly covered: test_updateStatistics_counts_zero_for_empty_template.

== Demo and Q/A ==

Approve the oldest translation template uploads awaiting review. The requests should not time out.

Run cronscripts/rosetta-pofile-stats.py. This will take hours. Afterwards, POFile translation statistics will still be valid (and not all zeroed out as would happen if the change kicked in inappropriately).

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/model/pofile.py
  lib/lp/translations/model/potemplate.py

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/translations/model/pofile.py'
--- lib/lp/translations/model/pofile.py 2011-01-26 23:33:20 +0000
+++ lib/lp/translations/model/pofile.py 2011-02-21 20:08:53 +0000
@@ -825,6 +825,12 @@
825825
826 def _countTranslations(self):826 def _countTranslations(self):
827 """Count `currentcount`, `updatescount`, and `rosettacount`."""827 """Count `currentcount`, `updatescount`, and `rosettacount`."""
828 if self.potemplate.messageCount() == 0:
829 # Shortcut: if the template is empty, as it is when it is
830 # first created, we know the answers without querying the
831 # database.
832 return 0, 0, 0
833
828 side_traits = getUtility(ITranslationSideTraitsSet).getForTemplate(834 side_traits = getUtility(ITranslationSideTraitsSet).getForTemplate(
829 self.potemplate)835 self.potemplate)
830 has_other_msgstrs = "COALESCE(%s) IS NOT NULL" % ", ".join([836 has_other_msgstrs = "COALESCE(%s) IS NOT NULL" % ", ".join([
@@ -889,6 +895,12 @@
889895
890 def _countNewSuggestions(self):896 def _countNewSuggestions(self):
891 """Count messages with new suggestions."""897 """Count messages with new suggestions."""
898 if self.potemplate.messageCount() == 0:
899 # Shortcut: if the template is empty, as it is when it is
900 # first created, we know the answers without querying the
901 # database.
902 return 0
903
892 flag_name = getUtility(ITranslationSideTraitsSet).getForTemplate(904 flag_name = getUtility(ITranslationSideTraitsSet).getForTemplate(
893 self.potemplate).flag_name905 self.potemplate).flag_name
894 suggestion_nonempty = "COALESCE(%s) IS NOT NULL" % ', '.join([906 suggestion_nonempty = "COALESCE(%s) IS NOT NULL" % ', '.join([
895907
=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py 2011-02-14 17:14:17 +0000
+++ lib/lp/translations/model/potemplate.py 2011-02-21 20:08:53 +0000
@@ -1155,8 +1155,7 @@
1155 if shared_template is template:1155 if shared_template is template:
1156 continue1156 continue
1157 for pofile in shared_template.pofiles:1157 for pofile in shared_template.pofiles:
1158 template.newPOFile(1158 template.newPOFile(pofile.language.code, create_sharing=False)
1159 pofile.language.code, False)
1160 # Do not continue, else it would trigger an existingpo assertion.1159 # Do not continue, else it would trigger an existingpo assertion.
1161 return1160 return
11621161
11631162
=== modified file 'lib/lp/translations/tests/test_pofile.py'
--- lib/lp/translations/tests/test_pofile.py 2011-02-17 22:06:02 +0000
+++ lib/lp/translations/tests/test_pofile.py 2011-02-21 20:08:53 +0000
@@ -760,6 +760,18 @@
760 # A `POFile` starts out with consistent statistics.760 # A `POFile` starts out with consistent statistics.
761 self.assertTrue(self.factory.makePOFile().testStatistics())761 self.assertTrue(self.factory.makePOFile().testStatistics())
762762
763 def test_updateStatistics_counts_zero_for_empty_template(self):
764 # Statistics for an empty template are all calculated as zero.
765 pofile = self.factory.makePOFile()
766 pofile.updateStatistics()
767 self.assertEquals(0, self.devel_pofile.messageCount())
768 self.assertEquals(0, self.devel_pofile.translatedCount())
769 self.assertEquals(0, self.devel_pofile.untranslatedCount())
770 self.assertEquals(0, self.devel_pofile.currentCount())
771 self.assertEquals(0, self.devel_pofile.rosettaCount())
772 self.assertEquals(0, self.devel_pofile.updatesCount())
773 self.assertEquals(0, self.devel_pofile.unreviewedCount())
774
763 def test_updateStatistics(self):775 def test_updateStatistics(self):
764 # Test that updating statistics keeps working.776 # Test that updating statistics keeps working.
765777