Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
The problem
Drupal 8 is currently slow, and has a lot of cache gets. While we may be able to roll some of them in to multi-gets, and may be able to make Drupal 8 generally faster, we would like to have a very fast cache solution.
Proposed solution
Add a chained, fast cache implementation. The docblock of the implementation reads thusly:
/**
* Defines a backend with a fast and a consistent backend chain.
*
* In order to mitigate a network roundtrip for each cache get operation, this
* cache allows a fast backend to be put in front of a slow(er) backend.
* Typically the fast backend will be something like APCu, and be bound to a
* single web node, and will not require a network round trip to fetch a cache
* item. The fast backend will also typically be inconsistent (will only see
* changes from one web node). The slower backend will be something like Mysql,
* Mecached or Redis, and will be used by all web nodes, thus making it
* consistent, but also require a network round trip for each cache get.
*
* It is expected this backend will be used primarily on sites running on
* multiple web nodes, as single-node configurations can just use the fast
* cache backend directly.
*
* We always use the fast backend when reading (get()) entries from cache, but
* check whether they were created before the last write (set()) to this
* (chained) cache backend. Those cache entries that were created before the
* last write are discarded, but we use their cache IDs to then read them from
* the consistent (slower) cache backend instead; at the same time we update
* the fast cache backend so that the next read will hit the faster backend
* again. Hence we can guarantee that the cache entries we return are all
* up-to-date, and maximally exploit the faster cache backend. This cache
* backend uses and maintains a "last write timestamp" to determine which cache
* entries should be discarded.
*
* Because this backend will mark all the cache entries in a bin as out-dated
* for each write to a bin, it is best suited to bins with fewer changes.
*
* @ingroup cache
*/
Comment | File | Size | Author |
---|---|---|---|
#97 | 2231595-reorder_followup-97.patch | 1.25 KB | yched |
#92 | interdiff.2231595.txt | 6.37 KB | Anonymous (not verified) |
#92 | 2231595-91.patch | 13.81 KB | Anonymous (not verified) |
#88 | 2231595-87.patch | 12.87 KB | Anonymous (not verified) |
#87 | 2231595-81.patch | 13.4 KB | Anonymous (not verified) |
Comments
Comment #1
catchComment #2
Anonymous (not verified) CreditAttribution: Anonymous commentedyay, thanks for opening this.
Comment #3
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #4
Anonymous (not verified) CreditAttribution: Anonymous commentedhere's a PoC patch i discussed with msonnabaum a bit. posting here so other people can take a look.
Comment #5
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #6
BerdirWithout looking at this in detail, just wondering if we could somehow integrate this into the existing CacheBackendChain that we have but never use right now.. Seems like this would be an important feature to reliably use that anyway, at least when you have multiple servers?
Comment #7
Anonymous (not verified) CreditAttribution: Anonymous commentedooookay, here's a patch that fixes the new backend, adds an apc backend and factory factory factory factory, and wires it together in core.services.yml.
really not happy with how this looks, but i don't see a better way to get the level of runtime configurability we need with static yml.
to test this out, add this to settings.php
Comment #8
Wim LeersThis looks great, but could use some more scrutiny from others :)
I fixed all the nitpicks I could find. But I also had trouble understanding
ChainedConsistentAndInconsistentBackend
… so I added a paragraph in the class's docs, explaining the algorithm used. In doing so, I got actually even more confused by the fact that there was both$this->consistencyTimestamp
and$this->lastSetTimestamp
. Because, really, you only need *one*! I also found "consistency timestamp" to be a rather confusing term: to me, it means "this is the last known time where the inconsistent backend is fully consistent with the consistent backend". But that's not at all what it does. All it does is tracking the timestamp after which the cache entry in the inconsistent backend must be created, if not, then it's stale/wrong/inconsistent. Therefore, I consolidated those two variables into a single one called$this->lastWriteTimestamp
. Hopefully I'm not the only one who thinks that's less ambiguous. (If not, you can easily restore the original variable name, at least there's now only 1 instead of 2 variables.)Maybe
ApcuBackend.php
orApcUserBackend.php
would be a better name?This counts the data in *all* APC cache bins, rather than the current one?
Nice catch!
But… shouldn't this have been caught a long time ago by test coverage? Scary… :(
Why doesn't this subclass
\Drupal\Core\Cache\BackendChain
? I think it's becauseBackendChain
"stupidly" chains cache back-ends, not allowing for inconsistent cache back-ends, only allowing for consistent-but-automatically-discarding-cache-entries cache back-ends (i.e. LRU/MRU/…)?If so, it'd be great if this were documented on
ChainedConsistentAndInconsistentBackend
.Comment #9
Berdir3: #2233337: Impossible to specify per-bin cache backend service from within settings.php. The bug was introduced in the cache.default issue, the bins subkey didn't exist before that.
Comment #10
Wim LeersIn chat, beejeebus pointed me to a silly mistake.
Comment #11
msonnabaum CreditAttribution: msonnabaum commentedHere's an updated that adds a couple small tests and fixes what I believe would have been a bug in getMultiple. I think the new backend wrappers need to be renamed, but holding off for now to keep the interdiff sane.
Comment #12
msonnabaum CreditAttribution: msonnabaum commentedI may have been wrong about my changes to getMultiple in #11. I was convinced it was necessary this morning, but after talking with beejeebus, I think I may have been confused.
Comment #13
Steven Merrill CreditAttribution: Steven Merrill commentedSimple change fixing the tests from #11.
Comment #15
Steven Merrill CreditAttribution: Steven Merrill commentedImplementing Mark's TODO using a mock for the inconsistent cache.
Comment #16
Anonymous (not verified) CreditAttribution: Anonymous commentedthanks for adding tests.
updated patch keeps the tests, and reverts the getMultiple() changes, and renames the cache to InconsistentBackend.
Comment #17
Wim Leers#16: can you re-post that interdiff with
git diff -M
? Then we can see what *really* changed between the diffs — thanks :)Comment #18
Anonymous (not verified) CreditAttribution: Anonymous commentedok, moar speed.
i've pulled in the patch over at #2229283: Menu tree caching is broken, because that messes up the primed cache case.
also, changed the cache created to a numeric type with three decimal points, because 1 second granularity very badly impacts on the inconsistent vs consistent cache hit ratio.
with those changes, and this config setting:
front page, admin user, default standard in stall with no content.
we go from this [REMOVED]
to this: [REMOVED]
Comment #20
catchThis method should never have been added to the interface, I think it's used in one or two tests at most. I have a vague memory of opening an issue to remove it, but can't find right now.
Good change. That chance of a (almost) full second where cache writes would be rejected was nagging a bit, just using microtime seems better.
Have been trying to think what happens when servers running the local cache get out of sync in terms of time. Let's take two servers:
Server Correct
Server Behind (running 10 seconds behind because NTP isn't configured or something)
Server Correct writes an item to the consistent cache with a timestamp of 123400000, the last write timestamp is set to 123400000. Various local caches pick this up.
100ms later, server Behind writes an item to the consistent cache with a timestamp of 123399990, the last write timestamp is set to 123399990.
Now the item that was locally propagated to local caches with a created timestamp of 123400000 is valid for a full ten seconds after it should have been invalidated, because the timestamp is later than the 'last write timestamp', even though in reality it was created earlier!
One option to fix this would be instead of comparing the created timestamp, we add an additional property to the local cache items (like consistencyId or something) when those are written locally, and always compare that to the last write timestamp that's fetched from the consistent backend.
That way we're always checking the consistent value at the time the item was written locally, against the consistent value as it currently is - there's no information that relies on the local server being in sync at all. The last_write_timestamp could still be 'wrong', but it doesn't matter because we're just using it as a unique ID - could just as well be a random hash.
Comment #21
Anonymous (not verified) CreditAttribution: Anonymous commenteddiscussed this a bit with catch in IRC - i think ntp problems are out of the scope of Drupal.
if that's broken for your web cluster, you have bigger problems.
Comment #22
Anonymous (not verified) CreditAttribution: Anonymous commentedthis should fix the fails, interdiff is (make sure we pass an int to createFromFormat):
Comment #24
Anonymous (not verified) CreditAttribution: Anonymous commentedok, this time without the durp durp rebase.
Comment #25
Anonymous (not verified) CreditAttribution: Anonymous commentedooh, setMultiple went in, so this patch implements it.
Comment #28
Anonymous (not verified) CreditAttribution: Anonymous commented25: 2231595-25.patch queued for re-testing.
Comment #30
Anonymous (not verified) CreditAttribution: Anonymous commentedfixed signature mismatch with setMutiple().
Comment #32
Anonymous (not verified) CreditAttribution: Anonymous commentedmany yaks were shaved to bring you this interdiff.
Comment #34
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #35
Anonymous (not verified) CreditAttribution: Anonymous commentedwoops, i somehow lost the unit test. this patch is the same as #34, but with the unit test added back.
Comment #37
Steven Merrill CreditAttribution: Steven Merrill commentedNow with fewer lulz and hopefully all tests passing.
Comment #38
Steven Merrill CreditAttribution: Steven Merrill commentedComment #39
Steven Merrill CreditAttribution: Steven Merrill commentedSince #37 passes testing, I took this for a spin.
After installing, I put the required
into settings.php, and I am registering APC cache hits. I took the /admin/people page on a fresh install, dumped APC user cache, and then requested it 6 times. On the subsequent 5 page loads, I got 35 cache hits each time from APC, as the attached apc.php screenshot shows.
Unfortunately, I don't think we can quite turn this on by default. Starting with 8.x core with the patch applied and adding the cache line to a fresh settings.php, I get this exception after entering MySQL information into the installer. Looks like KeyValueStore can't work with no DB. What would be the easiest way to short-circuit this in the installer?
Comment #40
Steven Merrill CreditAttribution: Steven Merrill commentedIn IRC, beejeebus points out that we're not yet trying to make this the default cache backend, so I'll set this back to Needs Review.
If this becomes the default cache backend, we may be able to check for MAINTENANCE_MODE === "install" in the factory and return a regular database cache when that is the case to fix the installer.
Comment #41
catchThere's a couple of installer options:
1. Use a null/memory key/value store for the initial install screens, similar to the null cache backend.
2. If we have a problem later in the installer, make the key/value store responsible for its own storage in the same way that cache backends currently are (right now it's still defined in system_schema(). Should be doing that anyway I think.
Comment #42
Anonymous (not verified) CreditAttribution: Anonymous commentedcan we split the installer stuff off to another issue? i'm going to replace the APC backend with a php one today, then i think this is ready.
Comment #43
catchYes that's definitely for another issue.
Comment #44
Wim LeersI agree, the installer stuff definitely belongs in another issue.
The wording here is sloppy, what about something like: "[…] HTTP's caching mechanism works with timestamps with precision up to a second, so perform the necessary rounding."
The comment says rounding should be applied, but we're really just dropping all decimals.
These are unnecessary;
drupal_serve_page_from_cache()
already sets these.This change implies no ETag header is being set anymore in the case page caching is disabled (globally or on the current page).
There was a todo to remove it, but is this really the right place to be changing this?
Overall, it seems all of the changes in
FinishResponseSubscriber
are out of scope?Comment #45
Steven Merrill CreditAttribution: Steven Merrill commentedI agree that we can probably not worry about messing with
drupal_serve_page_from_cache()
more than is necessary until #2016629: Refactor bootstrap to better utilize the kernel lands.The attached patch rolls back any changes in FinishResponseSubscriber and tries to have clearer language regarding the HTTP cache timestamps.
Comment #46
Steven Merrill CreditAttribution: Steven Merrill commentedComment #48
Steven Merrill CreditAttribution: Steven Merrill commentedWell, testbot says we do need those items, so here's the patch with them added back in.
Comment #49
Wim Leers#48: I think #45 only failed because it restored
$response->setLastModified(new \DateTime(gmdate(DATE_RFC1123, (int) $cache->created)));
, which uses$cache->created
as the number of seconds since epoch, but now$cache->created
is actually 1000 times larger (microtime(TRUE)
), so a simple division by 1000 should fix #45.Comment #50
moshe weitzman CreditAttribution: moshe weitzman commented48: 2231595-48.patch queued for re-testing.
Comment #52
Anonymous (not verified) CreditAttribution: Anonymous commentedreroll now the APCu backend landed, just to keep this going.
would still like #2259823: Make cache->created be to the millisecond to land first.
Comment #53
Anonymous (not verified) CreditAttribution: Anonymous commentedanother reroll now that #2259823: Make cache->created be to the millisecond has landed. yay for a shrinking patch.
Comment #54
sunRemoved an embedded image in comment #18 by @beejeebus, which appears to be a corrupted screenshot; the file contains additional garbage of ~25 MegaBytes (which has been downloaded by everyone who visited this issue).
Comment #55
Anonymous (not verified) CreditAttribution: Anonymous commentedyay, more shrinkage.
the patch is now the service definition, factory and service itself.
Comment #56
Wim Leerss/a/an/
Missing docs.
But more importantly: why even have this method, if it's called only once and its body is a single statement? :)
s/Mark/Marks/
(Always 3rd person singular.)
Won't this always evaluate to true?
I also prefer "cache entries", but
CacheBackendInterface
calls them "cache items", so let's use that too for consistency.This modifies the incoming
&$cids
parameter by reference…That parameter should be modified to only contain those cache IDs of the cache items that are successfully returned. I don't think that's happening right now.
(And if I'm right, then
GenericCacheBackendUnitTestBase
's coverage is lacking.)(I have the feeling I already pointed this out before, but I can't find it in this issue, so it must've been another issue.)
80 cols.
Docs incomplete.
{@inheritdoc}
A
@covers
docblock might be sufficient in this and other cases.This confused me so much, that I think that
ChainedBackend
might be a better name for this new cache back-end.I'm missing an assertion here that the consistent cache back-end never gets called. (i.e. no fallthrough)
This does not test a get (read) fallthrough, but a set (write) fallthrough.
I think we should test both.
Comment #57
Anonymous (not verified) CreditAttribution: Anonymous commentedre. 4: in a single webhead, perfect-clock-that-never-changes-time world, sure ;-) i could add a comment if you like? i added this:
re. 6: nope, you got that backwards. $cids should be modified to reflect the list of cache items that are not found. in this case, we have to the cid of any item we found in the inconsistent cache back if it is out of date.
updated patch handles the first 7. points - will discuss the test stuff in IRC. needs review to make sure i didn't break anything.
Comment #58
kim.pepperThis adds a test for when we have an inconsistent cache hit, the consistent cache does not get called.
Also a few code style cleanups.
Comment #59
Wim Leers#57.4: hah, of course — I *knew* I was overlooking something. Comment helps, thanks.
#57.6: Ok then. Must've misread that also. I just remembered we have test coverage for this, so surely it must be correct.
"server", "webheads" and "webserver" — let's use consistent terminology?
Also, "clock drift" is the correct term, not "clock shift".
#58: thanks!
s/cache/cache backend/
s/inconsistent cache/inconsistent cache backend/
s/backend/cache backend/
If we want to test that a cache get does *not* hit the consistent cache back-end, then why would we want to store an outdated cache item in the inconsistent cache back-end?
This comment is wrong.
This is not used anywhere.
Extraneous newlines.
Comment #60
kim.pepperThanks Wim. I've made fixes for #59.
Comment #61
Wim LeersLeaving at NR to gather some more feedback.
I think the code looks fine overall. But I'm still not too fond of the name, I think it's rather confusing. I think
ChainedBackend
might be better.One remaining stupid nitpick that can definitely be ignored if no more rerolls are needed:
Missing newline.
Comment #62
pounardA backport of this issue to D7 is welcome, there's a few modules that are confronted to the cache consistency problem (such as APC) and having a generic solution in core would be a good idea.
Comment #63
kim.pepper@pounard Beejeebus has a D7 port already at https://drupal.org/project/schrodicache
Comment #64
pounardWould be nicer to have in core I meant !
Comment #65
David StraussA big +1 on having deep support for inconsistent caching. On Pantheon's Redis boxes, we know they're full when they saturate their network connections. Reducing the common case to just checking for changes rather than pulling the actual data would help stuff scale much better. It would also probably make the MySQL/MariaDB plus local APC/APCu work really well even for huge sites.
Comment #66
kim.pepperRe-roll and fix for #61.
Comment #67
effulgentsia CreditAttribution: effulgentsia commentedPatch implementation doesn't match the issue summary. If the implementation is what's desired, can we get an updated summary? Specifically:
In the patch, InconsistentBackend does not extend BackendChain. Would be good for the summary to explain why: I'm guessing something related to a need for timestamp checking making none (or not enough of) the methods reusable, but if there's another reason, would be good to have it documented in the summary.
Patch implements timestamp in state rather than in the caches. Why?
Also:
Why do we need this controllable from settings.php? Why not pass the 2 services as constructor dependencies in the core.services.yml entry?
More consistent with other backends to rename this to
cache.backend.inconsistent
? Or, if we do the suggestion above, then perhaps evencache.backend.inconsistent.apcu.database
? People can then create differently named services for different backend combinations. Not sure if that level of specificity in the service name would be useful or not.Comment #68
Wim Leers#67: both of your "also" points sound great.
Comment #69
yched CreditAttribution: yched commentedAwesome feature, but the terminology seems confused atm. The patch uses "inconsistent backend" termonology for both the "local, fast but possibly outdated backend" and the "wrapper around a chain of the former and a remote, reliable backend" (see class name, property names inside that class, service and factory names).
Also, method order in the class feels a bit random ?
Comment #70
Anonymous (not verified) CreditAttribution: Anonymous commentedthis patch does cache_inconsistent_factory -> cache.backend.inconsistent.
i don't know how to do #67.2.
as for naming - totally open to suggestions.
FastCache ?
Comment #71
Anonymous (not verified) CreditAttribution: Anonymous commentedrenamed InconsistentBackend to ChainedFastBackend.
renamed inconsistent to fast within ChainedFastBackend.
interdiff attached.
Comment #72
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #73
msonnabaum CreditAttribution: msonnabaum commentedBecause that is not ephemeral data.
Comment #74
effulgentsia CreditAttribution: effulgentsia commentedHow so? It can safely get dropped, and that just means you need to fetch from the slower cache. Meanwhile, why make a cache backend have a dependency on a non-ephemeral storage service that is potentially slower than even the slow cache?
Comment #75
msonnabaum CreditAttribution: msonnabaum commentedAt some point I remember talking through this with Justin and at the time, losing the timestamp meant it could result in inconsistent data being read. Either I was wrong or the design changed since then, because looking at it now, it does seems fine if it goes away.
It's still a bit of a micro-optimization though, so I dont really care if that gets changed.
Comment #76
effulgentsia CreditAttribution: effulgentsia commented#39 indicates it could solve a functional issue related to being able to actually use this by default, not just a micro-optimization, but not sure if that comment is still relevant.
What do you all think of
SynchronizedBackend
andcache.backend.synchronized
as names? I don't think we need the word "chained" in there, since this isn't really a chain, in that it contains only two "links" rather than an arbitrary number, and the two links are not equivalent: one is authoritative and the other isn't.Comment #77
kim.pepperLike the new naming. It makes more sense to me.
Found a few minor issues:
Missing variable name
Missing variable name
This method doesn't exist on the interface.
Class name doesn't match file name.
Comment #78
Anonymous (not verified) CreditAttribution: Anonymous commentedre. Synchronized, don't care. i'll reroll with whatever is decided.
i don't care about state vs cache. i don't think #39 is remotely relevant. we need the db for cache as well, and if we can't have it in the installer, we do something else. we can do the same with keyvalue. but also, i don't care. i'll reroll without state.
Comment #79
msonnabaum CreditAttribution: msonnabaum commentedNot sure SynchronizedBackend adds much over ChainedFastBackend. It doesn't really imply what's happening.
It's true that chained isnt totally accurate, but if we're going for accuracy, it's more of a write-through backend or something.
Comment #80
yched CreditAttribution: yched commentedAdding to the naming brainstorm : the main idea is "fast with fallback", with the rest being implementation details ?
Also, +1 on the timestamp management being handled by the "fast backend" internally in its own storage without depending on an external system like state() That probably means that it needs to be slighty more than a CacheBackendInterface, though.
--> FastCacheBackendInterface extends CacheBackendInterface ?
Comment #81
pounard@#74, #75, #76
One of the abilities of Drupal 7 was to be able to bootstrap without database until it gets after the page cache stuff, so I guess in that regard keeping the timestamps into the cache backend itself makes sense, but in Drupal 8 the K/V store that serves for the state API is supposed to be pluggable, so problem solved. You can safely plug the K/V store in a faster backend, even the same as the cache I suppose.
Comment #82
Anonymous (not verified) CreditAttribution: Anonymous commentedupdated for #77. interdiff below.
Comment #83
yched CreditAttribution: yched commentedRe my own #80
Scratch that of course, the timestamps can't be stored by the "fast, local" backend, precisely since they have to be shared between web heads.
Still, +1 on having the ChainedFastBackend wrapper store them as a special "housekeeping" entry in the "remote, consistent" backend rather than in state(). The timestamps are metadata about the entries in the consistent backend when it's used as fallback for a faster backend, it seems only natural to store them along with those entries ?
re @pounard #81 : sure, storing them in state() works functionnally, and you can indeed switch the state() k/v backend to keep the whole thing efficient, but then it means this "fast cache with fallback" feature de facto constrains where you put your (whole) state store. Why would we choose to couple with state() if we can have a purely standalone system ?
Comment #84
pounardYes, I do agree with that last statement, indeed.
Comment #85
catchFine with putting the timestamp in the central cache. Since we treat the entire local cache as invalid if the timestamp is newer or/can't be found, that'll work fine.
Comment #86
Anonymous (not verified) CreditAttribution: Anonymous commentedmkay, i'll reroll with the timestamps in the consistent cache backend.
Comment #87
Anonymous (not verified) CreditAttribution: Anonymous commentedrerolled to use the consistent backend instead of State to track the timestamp.
Comment #88
Anonymous (not verified) CreditAttribution: Anonymous commenteddurp durp durpal uploaded the wrong patch, disregard #87.
Comment #89
yched CreditAttribution: yched commentedCode looks fine to me :-)
Nitpick / docs review :
a fast and *a* consistent backend ? Otherwise it reads like its the same backend that is both fast and consistent :-)
+ In order to grasp the full picture, "one fact and one consistent" is a bit short, the body of the doc could first expand the characterization of the backends :
- one backend is fast (typically APCu) but inconsistent (local to each web head).
- one backend is slower (the usual cache backends like sql or memcache), but consistent (shared between web heads).
Then the following paragraph explains the sync and "last write timestamp" logic alright, but without the above it's not really clear why that logic is even needed.
It would also make it clearer than this "chained backend" is only useful on sites with several web heads, and that sites with a single head might as well use the fast, local backend directly ?
80 chars
80 chars :-/ Lose the comma ?
method order is a bit random, could we reorder those ?
remnant of 'inconsistent'. This is now called "chained fast backend".
3rd person.
+ The rest of the code uses this method as a getter, not as an initializer.
--> getLastWriteTimestamp() ?
(then do we need a setter for consistency ?)
I think we favor "do not", "are not", ... over "don't", "aren't", ... in comments.
Comment #90
yched CreditAttribution: yched commentedAlso, looks like this chained backend is great for "registry"-like cache bins (e.g plugin discovery), where the set of entries is relatively stable over a period of time, but probably not so much for stuff like the page cache or entity / entity render cache, where new entries will constantly invalidate the local cache anyway ?
If the above is correct, might be worth mentioning as a hint in the class doc ?
Comment #91
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #92
Anonymous (not verified) CreditAttribution: Anonymous commentedaddresses #89 and #90.
Comment #93
yched CreditAttribution: yched commentedThanks !
is still an unnatural / intertwined order that doesn't really help when reading the code in the class :-p
No need to block commit for this though.
Looks good to me, but I jumped pretty late in the issue, so I'll let others confirm the RTBC.
Comment #94
moshe weitzman CreditAttribution: moshe weitzman commentedI think we are finally RTBC here. if anyone re-rolls they can do the method reorder per #93
Comment #96
catchYes the method re-ordering can happen in a follow-up.
Happy with this now, so committed/pushed to 8.x, this is 100% a new feature, so not sure it merits a change record (although if someone wants to write one they could).
Comment #97
yched CreditAttribution: yched commentedquick followup for method reorder.
Comment #98
moshe weitzman CreditAttribution: moshe weitzman commentedThanks yched
Comment #99
alexpottFixed the whitespace issues in the patch on commit.
Committed e2800b2 and pushed to 8.x. Thanks!
Comment #102
Wim LeersFollow-up issue: #2541432: Follow-up for #2231595: Document why ChainedFastBackend cannot use BackendChain.