Drupal\KernelTests\Core\Entity\ContentEntityChangedTest::testChanged
When editing a non-translatable field the updated changed time is equal across all translations.
Failed asserting that 1487987261 matches expected 1487987260.
https://www.drupal.org/pift-ci-job/608035
https://www.drupal.org/pift-ci-job/615761
https://www.drupal.org/pift-ci-job/627979
https://www.drupal.org/pift-ci-job/661431
https://www.drupal.org/pift-ci-job/672309
https://www.drupal.org/pift-ci-job/681334
https://www.drupal.org/pift-ci-job/736994
https://www.drupal.org/pift-ci-job/742521
https://www.drupal.org/pift-ci-job/744664
https://www.drupal.org/pift-ci-job/750416
https://www.drupal.org/pift-ci-job/754089
https://www.drupal.org/pift-ci-job/758910
https://www.drupal.org/pift-ci-job/761965
Problem/Motivation
ChangedTestItem::preSave() uses time() instead of REQUEST_TIME. This leads to a fail in the ContentEntityChangedTest::testChanged, when the changes of entities occurs at the boundary between two seconds (see #15 with milliseconds outputs).
Example:
- original instance has been changed in 1490000000,999
- translation instance has been changed in 1490000001,000 (because it does not happen instantly)
It may seem that this is a rare fluke that is not worth the attention, but many examples lost RTBC status due to it.
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #32 | 2857843-31.patch | 1.19 KB | jofitz |
Comments
Comment #2
mpdonadioOddly, I thought this was a second rollover problem, but this doesn't seem to fail locally for me...
Comment #3
mpdonadioComment #4
gambryMaybe a specific problem from a specific comming?
I'm testing locally against 59b450939fe923baf5416a4d6bab742fdb5e68c0 , which looks like being last change on ContentEntityBase before that test ran on 24th of Feb.
Comment #5
gambryI've tested patch on #2 against commits:
Issue #2808335 by hchonov, tstoeckler, anish.a, Wim Leers, catch, Berdir: Changed time not updated when only non-translatable fields are changed on a translated entity
22 February 2017 16:45:04 GMT
Issue #2854926 by xjm, himanshu-dixit, borisson_, boaloysius, alexpott: Remove unneeded control structures in ContentEntityBase
23 February 2017 23:20:26 GMT
Issue #2854249 by GoZ, himanshu-dixit, boaloysius, xjm: Default message for linkNotExists and linkByHrefNotExists is wrong
23 February 2017 11:09:38 GMT
To understand if was a failure due a regression / bugged new change (Test ran the 24th of Feb), but I couldn't replicate the error neither using sqlite nor mysql.
Comment #6
tacituseu commentedhttps://www.drupal.org/pift-ci-job/615761.
Comment #7
cilefen commentedI discussed this issue with @xjm, @cottser, @catch, @alexpott and @lauriii. We agreed this should be critical because random fails tend to block other work.
Comment #8
xjmThis happened also in https://www.drupal.org/pift-ci-job/627979 and https://www.drupal.org/pift-ci-job/615761 (just to confirm it wasn't a one-off failure).
Comment #9
mpdonadioEntityTestMulChanged::save() has a sleep(1) in it, and ChangedTestItem::preSave() has a call out to time() in it. I'm scratching my head wondering if the combo of these is fragile. I am also wondering why both are needed?
Comment #10
rachel_norfolkEffect also seen on https://www.drupal.org/pift-ci-job/672309
Comment #11
Anonymous (not verified) commentedLooks like this is really catch! There are a lot of operations to accumulate the necessary number of milliseconds, allowing you to jump to the next second between calls of
preSave()for each translation inContentEntityStorageBase::invokeFieldMethod().Comment #13
Anonymous (not verified) commentedOn my local machine difference between two calls of
preSave()(between translations entities) is 3 ms.For proof of concept, that it can lead to 1 second a difference:
In both cases are used milliseconds (limited by the int type) instead of seconds in 'changed' value for more information.
Or #11 enough to confirm the reason of fail?
Comment #15
Anonymous (not verified) commentedSorry. I forgot to turn off the second test (
testRevisionChanged) and as a result 10000_runs always failed (I aborted CI). Also, 2000 languages clearly go beyond time limit (even php7 failed, who would have known :).In order not to create unnecessary noise, I created a separate experiment for experiments. And this gave the results:
Comment #16
Anonymous (not verified) commentedMini idea just round microseconds. This should give a reserve of 1 second. But I'm not sure how this will affect other tests.
Comment #17
Anonymous (not verified) commentedOkay. It was a stupid idea - threshold simply shifted from .999 to .499 :(
And looks like it is not fandom fail, because preSave() is called for each translations and as a result it can give different time value.
Comment #19
mpdonadioI think this shows the problem. Gotta attach path first for dreditor.
Comment #21
mpdonadioWhen I run locally:
Here 1495242053 is the request time, and 1495242062 and 1495242061 and the changed times of the two entities we are mucking with.
Ok, here we are letting a second to pass to force so that the time() is always different when it gets called. Also notice that a whole bunch of time has passed in the test.
So, I think the problem is that getting the current time in this class is wrong. Instead we need to chase to the entity in original language and use its changed time instead of the current time.
Or, as I started to do with this patch, weave in the Time service, swap it out with test version so that we can adjust REQUEST_TIME through ContentEntityChangedTest.
?
Comment #23
Anonymous (not verified) commentedRepeating fails make one think about this assertion critically:
getChangedTime() returns the time in seconds. Therefore, by this assert (last in listing), we can at best be able to verify that the request for changing the $entity and $german in one second interval (that rather rough approximation).
Therefore, we can make:
ContentEntityStorageBase::invokeFieldMethod())#21:
Sounds nice irrespective of this issue!
Comment #25
Anonymous (not verified) commentedComment #26
gambryWorth having a look at: https://stackoverflow.com/questions/43499771/php-time-sleep-until-does-n...
And the different value of time() and microtime() when time_sleep_until() is used.
Comment #27
Anonymous (not verified) commentedThank you @gambry! This is useful. I tried to play with
microtime(true)&usleep(). But the code is more complicated because of this. Like this:So I left a more rude, but readable code:
time_sleep_until(time() + 1.1);Comment #28
Anonymous (not verified) commentedSorry, it was example with
microtime+time_sleep_until, but withusleepit works also unstable (just changetime_sleep_until($wait)tousleep($step))Comment #29
mpdonadioI think I like #25.
Comment #30
catch#25 looks good to me as well. Couple of suggestions on the comments:
Would reword the comment to:
"For the stability of the test, set the time of the original language to the current time plus just over one second to simulate two different request times. @todo mock the time service in [issue]"
Let's open an issue for using the time service too and link to that.
Wait 1.1 seconds because time_sleep_until() is not reliable.
Comment #31
jofitzComment #32
jofitzMade both changes requested by @xjm in #30 including opening an issue for using the time service: #2908210: Mock the request time instead of making test entities sleep.
Comment #33
Anonymous (not verified) commentedThank you, @catch and @Jo Fitzgerald for review and help with it.
It looks much better, but the one who will read this can have several questions:
EntityTestMulChanged::save()viasleep(1)?In fact, we care here precisely for the processing of all translations in the same second as the original instance.
Example:
As a result, the difference is one second, which leads to a fail in the test.
After patch we have:
This is only necessary for the test system, because it uses time() instead of REQUEST_TIME.
Comment #34
Anonymous (not verified) commented#33: What I wanted to say is that perhaps the commentary can be even clearer. But maybe not. The question of this does not block the issue. Whoever wants, will reads the issue in which these lines appeared :)
+1 to RTBC.
Comment #35
Anonymous (not verified) commentedAnd yep, #30 comment makes a lot of sense for another random fail https://www.drupal.org/pift-ci-job/743965:
We haven't issue for this fail, but the shortage of the current fix (additional wait of 1 second) should help with its. Woot!
Comment #36
mpdonadioComment #37
mpdonadioI think #36 demonstrates that the current patch will at least help with test stability, if not fix this problem outright.
Took another look at latest patch, and think it is OK. Reviewed file list, and I only had a few test-only patches on the issue, so my hands are clean. Let's do it.
Comment #38
catchI tried to think of a clearer comment but couldn't, and we have the time service follow-up which should make things a bit more self-explanatory anyway.
Committed/pushed to 8.5.x and cherry-picked to 8.4.x. Thanks!
Comment #42
xjm