Merge lp:~jtv/launchpad/bug-599254 into lp:launchpad/db-devel

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 9502
Proposed branch: lp:~jtv/launchpad/bug-599254
Merge into: lp:launchpad/db-devel
Diff against target: 90 lines (+21/-15)
2 files modified
lib/lp/translations/model/potmsgset.py (+8/-1)
lib/lp/translations/model/translationmessage.py (+13/-14)
To merge this branch: bzr merge lp:~jtv/launchpad/bug-599254
Reviewer Review Type Date Requested Status
Māris Fogels (community) rc Approve
Henning Eggers (community) code Approve
Review via email: mp+28618@code.launchpad.net

Commit message

Ensure defined flushing order when making TranslationMessages current.

Description of the change

= Bug 599254 =

When translating a given message in a given template to a given language, only one TranslationMessage can have the is_current flag set and only one can have the is_imported flag set. The database ensures this through unique constraints.

In the current code (which we are working to replace entirely) we have validator functions that keep us within the bounds of those constraints. Whenever we set a TM's is_current to True, the corresponding validator first finds the message that previously had the flag set, if any, and clears the flag there. Same for the is_imported flag. They also tell the ORM that the change clearing the old flag needs to be flushed to the database before the change setting the new one.

These validators only look for translations that are for the same template. This is not as simple as it seems: with message sharing, most messages will have their template set to None which means they're shared across templates. Things quickly get confusing when changes to these flags are combined with changes to the TranslationMessage.potemplate field. Bug 599254 involves violations of the unique constraints (i.e, oopses) as the ORM flushes changes in an inconvenient order.

In this branch I'm making a minimal change to define flush order in cases where we were already clearing flags but not running the affected TranslationMessages through the validators. There are no functional changes. It looks probable that re-ordering the changes to TranslationMessages could also have solved the problem, but that would involve a risk of unintended functional changes. Our "recife" project is going to replace the entire cluster of methods that triggers the problem, as well as the validators. Not a good time for finicky unpredictable tweaks.

In the validators I did have to add an extra check against defining flush order between a TranslationMessage and itself. Some of the tests triggered such an error at one point.

I found myself unable to come up with a sensible test for this that doesn't take a lot of work or duplicate lots of other tests. We already have tons of tests covering the methods involved, and those all pass with my change.

To test, run all translations tests but in particular:
{{{
./bin/test -vv -t potmsgset -t translationmessage
}}}

There was one piece of lint in code I haven't touched: an unnecessary assignment. I'm leaving it intact to avoid conflicts with the ongoing Recife work that will touch the same code.

Jeroen

To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :

Thanks for the quick fix.

57 + Store.of(self).add_flush_order(
58 + current_translation_message, self)

These two fit on one line, I think.

Cheers,
Henning

review: Approve (code)
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Indeed it does. Thanks for the review!

Revision history for this message
Henning Eggers (henninge) wrote :

Hi Maris,
could you please review this for an r-c? It fixes an oops that would hinder translators in their daily work.

Cheers,
Henning

Revision history for this message
Māris Fogels (mars) wrote :

Great work, nice explanation of the problem and solution. release-critical=mars

review: Approve
Revision history for this message
Māris Fogels (mars) :
review: Approve (rc)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/translations/model/potmsgset.py'
2--- lib/lp/translations/model/potmsgset.py 2010-03-22 17:18:28 +0000
3+++ lib/lp/translations/model/potmsgset.py 2010-06-28 12:21:37 +0000
4@@ -668,6 +668,9 @@
5 imported_message.is_imported = False
6 imported_message.is_current = False
7 imported_message.potemplate = None
8+ if imported_message != new_message:
9+ Store.of(imported_message).add_flush_order(
10+ imported_message, new_message)
11 if not (force_diverged or force_shared):
12 # If there was an imported message, keep the same
13 # divergence/shared state unless something was forced.
14@@ -682,10 +685,14 @@
15 # Change actual is_current flag only if it validates ok.
16 if new_message.validation_status == TranslationValidationStatus.OK:
17 if make_current:
18- # Deactivate previous diverged message.
19 if (current_message is not None and
20 current_message.potemplate is not None):
21+ # Deactivate previous diverged message.
22 current_message.is_current = False
23+ if current_message != new_message:
24+ Store.of(current_message).add_flush_order(
25+ current_message, new_message)
26+
27 # Do not "converge" a diverged imported message since
28 # there might be another shared imported message.
29 if not current_message.is_imported:
30
31=== modified file 'lib/lp/translations/model/translationmessage.py'
32--- lib/lp/translations/model/translationmessage.py 2010-03-12 15:13:14 +0000
33+++ lib/lp/translations/model/translationmessage.py 2010-06-28 12:21:37 +0000
34@@ -167,23 +167,23 @@
35 assert value is not None, 'is_current field cannot be None.'
36
37 if value and not self.is_current:
38- # We are setting this message as the current one. We need to
39- # change current one to non current before.
40+ # We are setting this message as the current one.
41 current_translation_message = (
42 self.potmsgset.getCurrentTranslationMessage(
43 self.potemplate,
44 self.language, self.variant))
45 if (current_translation_message is not None and
46+ current_translation_message != self and
47 current_translation_message.potemplate == self.potemplate):
48+ # Clear flag on the previous current message.
49 current_translation_message.is_current = False
50- # We need to flush the old current message before the
51- # new one because the database constraints prevent two
52- # current messages.
53- Store.of(self).add_flush_order(current_translation_message,
54- self)
55+ # Flush changes in the right order so we don't get two
56+ # current messages in the same place.
57+ Store.of(self).add_flush_order(current_translation_message, self)
58
59 return value
60
61+
62 def validate_is_imported(self, attr, value):
63 """Unset current imported message before setting this as imported.
64
65@@ -195,20 +195,19 @@
66 assert value is not None, 'is_imported field cannot be None.'
67
68 if value and not self.is_imported:
69- # We are setting this message as the current one. We need to
70- # change current one to non current before.
71+ # We are setting this message as the imported one.
72 imported_translation_message = (
73 self.potmsgset.getImportedTranslationMessage(
74 self.potemplate,
75 self.language, self.variant))
76 if (imported_translation_message is not None and
77+ imported_translation_message != self and
78 imported_translation_message.potemplate == self.potemplate):
79+ # Clear flag on the previous imported message.
80 imported_translation_message.is_imported = False
81- # We need to flush the old imported message before the
82- # new one because the database constraints prevent two
83- # imported messages.
84- Store.of(self).add_flush_order(imported_translation_message,
85- self)
86+ # Flush changes in the right order so we don't get two
87+ # current messages in the same place.
88+ Store.of(self).add_flush_order(imported_translation_message, self)
89
90 return value
91

Subscribers

People subscribed via source and target branches

to status/vote changes: