Problem/Motivation

The FastChainedCacheBackend is very useful provided that the data is mostly read, but not written often, as any write or clear leads to a full invalidation of the whole cache bin.

#2422657: Skip fast chained cache backend during installer improved the installer performance by removing the FastChainedCacheBackend during the installer, such especially saving the calls to markAsOutdated().

#2423591: Optimize config entity import sees the same problem with config entity import.

However it is possible that completely valid writes would lead to the same problem.

The FastChainedCache backend is at one point very ineffective and just leading to more database writes, than what it saves.

Proposed resolution: Shut off for a period of time

It makes sense hence to shut off the chained backend for periods of time and just use the consistent cache backend instead directly.

Shutting off is fortunately very easy:

Just writing a 'future timestamp' (e.g. 10 s into the future) and ensuring that a cache is never read / written or marked as outdated, when the current_ts < future_ts.

This is the first task to implement and should be fairly easy.

However what now remains is:

When should the fast backend be shut down?

In computer science a problem space like this is usually modeled via: "Oracle" (not the company) and the number of writes / time when the performance suffers is called the 'break-even' time.

Oracle knows all requests in advance and such can lead to an optimal policy, where the fast chained backend is shut off

Fortunately looking at the problem space like this, it can be compared with a device that 'overheats' when it gets too hot, where hot here is defined as the number of writes per time. This means the problem can be simulated well.

e.g. a very simple policy would be. If there is more than 20 writes within the interval of a second, then shut off for 10 seconds.

In our simple model this would mean:

- cool down temperature == 20 writes / second
- a threshold of e.g. 20 writes when we shut off
- a shut down time of e.g. 10s

whenever we write markAsOutdated() and are not in shutdown we:

- decrement the temperate by the cooldown * duration the backend was inactive
- increment the temperature per markAsOutdated request

Remaining tasks

- Discuss
- Prototype (simple)

- Implement a TemperatureAwareFastChainedCacheBackend extends FastChainedCacheBackend
- Implement shut off function in the that can be called with a time to shut off for.
- Ensure shut off is taken into account by read / write / markAsOutdated()

- Implement a generic 'SimpleCacheTemperature' component (as this could be useful for other caches as well)
- Implement a generic 'CacheTemperatureInterface' with the function calculateTemperature(duration, number_of_writes)
- Add as service cache.temperature.simple
- Inject service into the TemperatureAwareFastChainedCacheBackend

* Benchmark

User interface changes

- None

API changes

- None

Comments

catch’s picture

This looks like a good change to me, couple of comments:

1. I think we can just count writes/invalidations per-request rather than per-second. Module install and configuration sync are the most likely offenders and those generally happen within a single request. Also means nothing persistent to store.

2. We should try to avoid an extra consistent cache hit to get the shut off value - could possibly re-use the query for last write timestamp.

kim.pepper’s picture

Status: Active » Needs review
StatusFileSize
new3.51 KB

I've put together a very naive implementation. Just writes directly to the consistent cache if the threshold is reached.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Cache/TemperatureAwareChainedFastBackend.php
@@ -0,0 +1,77 @@
+/**
+ * A chained fast backend which shuts down fast cache if there are too many
+ * writes per request.
+ */
+class TemperatureAwareChainedFastBackend extends ChainedFastBackend {

We should certainly explain the idea implemented in that class

berdir’s picture

Do we really need this as a separate implementation? Shouldn't we just make it part of the existing class?

This isn't something that you want to explicitly enable IMHO, it should just work...

fabianx’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update
+++ b/core/lib/Drupal/Core/Cache/TemperatureAwareChainedFastBackend.php
@@ -0,0 +1,77 @@
+    if ($this->shutoff) {
+      return TRUE;
+    }
+    elseif ($this->writeCount >= $this->threshold) {
+      $this->shutoff = TRUE;
+    }
+    return $this->shutoff;

Just the >= check is enough and then just being two lines it can live directly in setMultiple().

Nice implementation though!

--

I agree with berdir though, lets just add this directly to the FastChainedCacheBackend given how simple the implementation here is.

CNW for simplifying that, but looks great overall.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new4.44 KB
new7.95 KB

Thanks for the feedback.

Moves the threshold logic into ChainedFastBackend and inlines condition.

kim.pepper’s picture

Spoke with @Fabianx at Drupalcon sprint, and agreed to write out how many writes we are doing per request during install to see what a good threshold is.

kim.pepper’s picture

Re-read the issue summary, and found we aren't using this in the installer as per #2422657: Skip fast chained cache backend during installer.

After installing, I couldn't find any good examples where we were writing to the cache more than 20 times per request, which seemed insignificant.

berdir’s picture

Did you try [Flush all caches] or manually clearing the cache tables?

One big problem is also cache tag invalidations. For example, try something like my field_map.php script in #2473983: [meta] Evaluate Entity Field API Scalability, there's a huge number of cache tag invalidations going on, and every time one happens, the fast chained invalidates, and that happens for any bin that's using it. Any ideas on how to improve that?

berdir’s picture

Status: Needs review » Needs work

I don't think this patch helps much at the moment.

Writing to the fast backend is fast. We don't need to worry about that.

The expensive part is markAsOutdated(), specifically in combination with cache tag invalidations as written above. Because every invalidation in turn calls that. Keep in mind that we do that for each fast chained bin. So by default, for every cache tag invalidation, we call markAsOutdated() on 3 different bins. And if you add more (I made default also fast chained on some of my sites as there is pretty much nothing left in there), that number increases.

So one thing we could try is something similar to DatabaseCacheTagsChecksum and avoid calling it repeatedly, unless we are fetching caches again. Will only work well if we don't fetch caches inbetween, need to do some tests on that.

berdir’s picture

Status: Needs work » Needs review
StatusFileSize
new1.52 KB

Something like this.

To test, e.g. put something like this in a test:

debug(ChainedFastBackend::$invalidatedCounter, 'current');
    $this->rebuildAll();
    debug(ChainedFastBackend::$invalidatedCounter, 'after rebuild');

In my case in FolderTest, an absolutely trivial test, the numbers went from ~320/380 to 70/79, this is already a nice improvement. I'd expect the number goes up the more modules are enabled, so we should also try something like that in a normal standard install.

Also, make sure you run the test in the browser UI ;)

Might want to open a separate issue, since this doesn't have anything in common really with this issue, but as written above, I'm not sure this issue can help much in general. We avoid the likely cheapest part of the whole thing only.

Status: Needs review » Needs work

The last submitted patch, 13: fast-chained-avoid-repeated-invalidation-2431259-13.patch, failed testing.

david_garcia’s picture

Status: Needs work » Needs review
StatusFileSize
new3.3 KB

I've been working on something for the ChainedFastBackend and came accross this issue.

The whole point of binary invalidation is to be able to have consistent and reliable cache values between environments (or "webheads"). If we only had one environment, we would actually not need this at all.

So why should we invalidate our own environment instead of only the other ones - if any. See patch attached - quite simple indeed- where we keep track of what "environment" has done the last binary invalidation and use this information not to invalidate the binary for the environment that has made the invalidation itself.

Indeed, I believe that with this change what happened in:

#2422657: Skip fast chained cache backend during installer

Would not have been needed at all as the install process happens on just on environment.

This is just a POF, maybe I got something wrong...

Another interesting change would be to move the actual binary invalidations to the end of the request, and do them all at once.

I'm setting this to needs review because I'm curious on seeing if this patch passes tests.

david_garcia’s picture

StatusFileSize
new3.31 KB

Ups...

The last submitted patch, 15: 2431259-chainedfast.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 16: 2431259-chainedfast.patch, failed testing.

david_garcia’s picture

Moving it away to it's own place #2611400: [meta] ChainedFastBackend should not invalidate the whole fastBackend when doing a Set()

Even if that ever ends up working, the aim of this issue is still completely valid.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.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.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.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.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.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.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.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.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should 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.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.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: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should 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: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

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

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

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should 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.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should 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.

Version: 9.5.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. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs work » Postponed (maintainer needs more info)
Issue tags: +stale-issue-cleanup

Thank you for creating this issue to improve Drupal.

We are working to decide if this task is still relevant to a currently supported version of Drupal. There hasn't been any discussion here for over 8 years which suggests that this has either been implemented or is no longer relevant. Your thoughts on this will allow a decision to be made.

Since we need more information to move forward with this issue, the status is now Postponed (maintainer needs more info). If we don't receive additional information to help with the issue, it may be closed after three months.

Thanks!

catch’s picture

This is probably a duplicate of #3526080: Reduce write contention to the fast and consistent backend in ChainedFastBackend at this point - the other issue is much newer and the approach is different, but it's trying to solve the same problem.

smustgrave’s picture

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

Works for me!

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.