Problem/Motivation

Quoting @lendude from #12

If you get into a state where lastWriteTimestamp is equal to the create timestamp in the entry, the ChainedFastBackend no longer knows what the sequence of events was, so it should default to the consistent cache, not the fast cache entry as it currently does.

In #2907728-37: Installer: Convert system functional tests to phpunit we are faced with random fail. Looks like that occurs due to incorrect works of ChainedFastBackend::markAsOutdated(). This is the code:

  /**
   * Marks the fast cache bin as outdated because of a write.
   */
  protected function markAsOutdated() {
    // Clocks on a single server can drift. Multiple servers may have slightly
    // differing opinions about the current time. Given that, do not assume
    // 'now' on this server is always later than our stored timestamp.
    // Also add 1 millisecond, to ensure that caches written earlier in the same
    // millisecond are invalidated. It is possible that caches will be later in
    // the same millisecond and are then incorrectly invalidated, but that only
    // costs one additional roundtrip to the persistent cache.
    $now = round(microtime(TRUE) + .001, 3);
    if ($now > $this->getLastWriteTimestamp()) {
      $this->lastWriteTimestamp = $now;
      $this->consistentBackend->set(self::LAST_WRITE_TIMESTAMP_PREFIX . $this->bin, $this->lastWriteTimestamp);
    }
  }

Here are some examples of last_write_timestamp_BIN_NAME values, when random fail appears.

d:1509618497.0820000171661376953125;
d:1509618497.131000041961669921875;

Modeling a possible situation:

  • server1 wrote last_write_timestamp_BIN_NAME in 1500000000.08200001
  • After 0.00001 seconds server2 call markAsOutdated()
  • If the server2 is behind by 0.00056789 seconds from server1, we get the following:
$now = round(microtime(TRUE) + .001, 3);
microtime(TRUE)= 1500000000.08200001 - 0.00056789 + 0.00001= 1500000000.08144212
microtime(TRUE) + .001 = 1500000000.08244212
round(microtime(TRUE) + .001, 3) = 1500000000.082

As result:
(server2) 1500000000.082 < 1500000000.08200001 (server1)
Despite the fact, that the code on the server2 was executed later.

Proposed resolution

Reject cache items from the fast backend when the created timestamp is exactly the same as the last_write_timestamp, this should almost never happen in production due to microsecond precision but it is not impossible - as demonstrated by the test coverage.

User interface changes

API changes

Data model changes

CommentFileSizeAuthor
#23 x1-2920590-23-test-0.000001.patch2.75 KBAnonymous (not verified)
#23 x1-2920590-23-test-0.00001.patch2.75 KBAnonymous (not verified)
#23 x1-2920590-23-test-0.0001.patch2.75 KBAnonymous (not verified)
#23 x1-2920590-23-test-0.001.patch2.75 KBAnonymous (not verified)
#22 x1-2920590-22-passes.patch2.75 KBAnonymous (not verified)
#22 x1-2920590-22-fails.patch2.75 KBAnonymous (not verified)
#21 interdiff-21-test-fix.txt620 bytesAnonymous (not verified)
#21 2920590-21-fix.patch2.23 KBAnonymous (not verified)
#21 interdiff-15-21-test.txt1.62 KBAnonymous (not verified)
#21 2920590-21-test.patch2.23 KBAnonymous (not verified)
#15 2920590-15.patch2.94 KBlendude
#12 2920590-12.patch2.57 KBlendude
#12 2920590-12-TEST_ONLY.patch1.84 KBlendude
#10 interdiff-and-fix.patch740 bytesAnonymous (not verified)
#10 2920081-retry-deal-with-equal-write-times.patch52.02 KBAnonymous (not verified)
#10 2920081-test-only.patch51.3 KBAnonymous (not verified)
#5 2920590-5.patch843 bytesAnonymous (not verified)
#2 interdiff.txt843 bytesAnonymous (not verified)
#2 demo-2907728-fix.patch52.85 KBAnonymous (not verified)
#2 demo-2907728-test-only.patch51.3 KBAnonymous (not verified)

Issue fork drupal-2920590

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Anonymous’s picture

vaplas created an issue. See original summary.

Anonymous’s picture

Issue summary: View changes
Status: Active » Needs review
StatusFileSize
new51.3 KB
new52.85 KB
new843 bytes
Anonymous’s picture

Issue summary: View changes

The last submitted patch, 2: demo-2907728-test-only.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Anonymous’s picture

Anonymous’s picture

Issue summary: View changes
Anonymous’s picture

Title: Increase the rounding precision in the ChainedFastBackend::markAsOutdated() » (canard?) Big Bos of Random Fails is ChainedFastBackend::markAsOutdated()
Issue summary: View changes

I just found out that round(4.444499999, 3); is 4.444 (I thought earlier that 4.445, my bad). So you can even increase the numbers in the examples, but it's not important :)

Refreshed title, because:

  1. the problem can occur in many tests in an unpredictable manner.
  2. the problem can not be reproduced by most machines (only on quick machines)
  3. only one unknown hero followed now (sorry for the permanent IS changes due to the numbers game)

It will be vexing if I sit down with this issue in a puddle, but I'm ready for it. Therefore, feel free to express criticism to clarify the situation.
lendude’s picture

This is some really impressive debugging. Nice work as always.

I think the fail/pass with the installer test is quite convincing, but.....is there any way we can write a less elaborate test for this? And something a little (ok, a lot) more explicit?

And I'm also wondering why this doesn't occur in WebTestBase install tests, are they just slower? Or are we doing something funky in BrowserTestBase?

Having an explicit test might help answer those questions too.

Anonymous’s picture

Status: Needs review » Needs work

@Lendude thanks for the support and great questions!

I completely agree with the fact, that while everything is rather turbid, and this needs to be clarified. If I have a clean test, I will definitely share it :)

And I'm also wondering why this doesn't occur in WebTestBase install tests, are they just slower? Or are we doing something funky in BrowserTestBase?

I just ran a series of tests with a specially downed markAsOutdated() calculation 0s, 0.002s, 0.01s, 0.1s, 1s, 10s and NULL (marks only if we haven't any values). See from #2920081-58: Ignore: patch testing issue .

I watched the tests burn in the fire and cried. But what's interesting, among all the chaos, the WTB:Installer tests passed as if nothing had happened!

Only 3 tests failed (with 10 and NULL variants), and only after the installation was completed, due to other reasons. See https://www.drupal.org/pift-ci-job/802608 (Installer.Installer section).

So, perhaps during installation WTB just ignores or permanently resets the bins.

Anonymous’s picture

Status: Needs work » Needs review
StatusFileSize
new51.3 KB
new52.02 KB
new740 bytes

Wow! @Lendude/@mixologic made a sharp turn of the plot!

Repost @Lendude's comment:

This is something @mixologic suggested on Slack when discussing this, the fastcache backend doesn't handle equal create/invalidate times.

And the proposal instead replaced one conditional in ChainedFastBackend::getMultiple():

-        if ($item->created < $last_write_timestamp) {
+        if ($item->created <= $last_write_timestamp) {

The last submitted patch, 10: 2920081-test-only.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

lendude’s picture

StatusFileSize
new1.84 KB
new2.57 KB

Ok so if you set things up like this, you have a failing test.

If you get into a state where lastWriteTimestamp is equal to the create timestamp in the entry, the ChainedFastBackend no longer knows what the sequence of events was, so it should default to the consistent cache, not the fast cache entry as it currently does.

But.....it's all well and good in a test scenario, but I don't actually see how you can end up in such a state in a real environment.

The last submitted patch, 12: 2920590-12-TEST_ONLY.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 12: 2920590-12.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

lendude’s picture

Status: Needs work » Needs review
StatusFileSize
new2.94 KB

#12 is failing randomly.... even locally it turns out. That doesn't make sense to me, but that's what happening.

Here is a version of the patch that I can consistently get to fail randomly (interesting sentence :-))

Running this locally gets me a fail rate of 25% to 50% fails.

The last submitted patch, 12: 2920590-12.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 15: 2920590-15.patch, failed testing. View results

Anonymous’s picture

Steep hit as always!

I can consistently get to fail randomly

Yep!)) Me too, failed/all: 12/20, 9/20, 9/20, 10/20, 11/20.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

berdir’s picture

\Drupal\Core\Cache\ChainedFastBackendFactory::__construct():

// Do not use the fast chained backend during installation. In those cases,
    // we expect many cache invalidations and writes, the fast chained cache
    // backend performs badly in such a scenario.
    if (!drupal_installation_attempted()) {
      $this->fastServiceName = $fast_service_name;
    }

So yes, we do not use the chained backend during the installer.

Anonymous’s picture

StatusFileSize
new2.23 KB
new1.62 KB
new2.23 KB
new620 bytes

#20: Thanks for the tip, @Berdir! Perhaps this is the wrong moment, because of which our converted tests work slower than WTB versiion (#2907728-76: Installer: Convert system functional tests to phpunit)?


However, #12 and #15 patches fall not on installation phase.

And if we add a bit of time, it works stably.

Here are the patches for testings, without fix from #12.

Anonymous’s picture

StatusFileSize
new2.75 KB
new2.75 KB

#21 with run-tests.sh tweak for a quick check.

Anonymous’s picture

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

It looks like there's a couple of approaches here, using <= seems like a good plan though. Tagging for issue summary update.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mstrelan’s picture

Status: Needs work » Postponed (maintainer needs more info)
catch’s picture

Status: Postponed (maintainer needs more info) » Needs review

I converted the test-only patch to an MR, (edit: unsuccessfully...).

The < check is still in core and I don't think it would hurt at all to change that to <=

#3498193: Optimize last_write_timestamp writes in ChainedFastBackend means that when there is one cache write on a page, the two servers would have to be more out of sync for there to be a problem, but it's only 50ms which is within NTP drift expectations, so it only makes the problem less likely and doesn't eliminate it. I think therre might be an issue around to try to use something else (like a special cache tag) that doesn't rely on clock time. The random failure discussed here appears to be something that isn't actually due to NTP clock sync at all but more rounding errors, with the 50ms ahead that's almost certainly solved now because it's further ahead, but still think changing to <= is more correct.

catch’s picture

Title: (canard?) Big Bos of Random Fails is ChainedFastBackend::markAsOutdated() » ChainedFastBackend should reject cache items from the fast backend that have identical timestamps to last_write_timestamp
Issue summary: View changes
Issue tags: -Needs issue summary update

The test only job didn't fail, but reverting the fix causes the new test to fail - here's the failing job:

https://git.drupalcode.org/project/drupal/-/jobs/4790389

borisson_’s picture

Status: Needs review » Needs work

There are more tests that are failing on the last test run. All in ChainedFastBackend.

The change however makes sense and it has good test coverage.

catch’s picture

hmm the new test that fails is the one that's being added so something is still wrong here.

catch’s picture

Status: Needs work » Postponed (maintainer needs more info)

Forgot about this issue but I think that #3526080: Reduce write contention to the fast and consistent backend in ChainedFastBackend makes this outdated now.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

catch’s picture

Status: Postponed (maintainer needs more info) » Closed (outdated)

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.