Problem/Motivation
#3438424: [random test failures] Race condition in state when individual keys are set with an empty cache attempted to fix a race condition with the State usage of CacheCollector by setting a cache item immediately after setting a new state value, so that requests that started with an empty cache wouldn't write to cache at the end at the end of the request, potentially writing back a cache item for the old state value before it was set.
This worked most of the time, but not in the case that the request that was attempting to set the new value failed to acquire the cache write lock in ::updateCache() (e.g. when another request was just writing to cache).
For this case, we want to write to the cache even if the lock can't be acquired, since our new value takes precedence over whatever's being written in the other request.
Steps to reproduce
Proposed resolution
Directly attempt to acquire the lock in State::set(), and write the cache whether or not the lock is acquired.
Additionally, run the cache invaidation logic in CacheCollector::updateCache() even if the lock isn't acquired, only cache sets are constrained by acquiring the lock now.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|
Issue fork drupal-3496257
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
Comment #2
catchComment #4
catchComment #6
luke.leberWhenever I see a call to wait on a lock I get a chill up my spine.
From a cursory glance, this change could result on a ton of processes spinning/waiting to acquire a lock under a heavy load, couldn't it?
I think this one should be subject to some load testing on a larger site.
Comment #7
catchI agree with this but I think we might not need the lock wait here, just to more aggressively clear/invalidate/set the cache even when we can't acquire the lock. Pushed a commit that tries this.
Comment #8
catchThe lock name was a red herring but I discovered some real race conditions.
https://git.drupalcode.org/project/drupal/-/jobs/3827879 runs a frequently failing tests that depends on writing to state 2500 times and is green with the MR.
https://git.drupalcode.org/project/drupal/-/pipelines/380036 is a full test run with only nightwatch failures, which this won't help with.
Comment #9
catchComment #10
godotislatelgtm
Comment #11
dwwThanks for this! Great work tracking this down.
I added some suggestions to the MR, mostly for trivial nits and a typo. Hate to slow down progress on a critical, but I really think we need comments about why we're not releasing the locks we're acquiring. I don't think this is RTBC if all the MR threads aren't resolved.
Comment #12
catchI'm going to spend a bit more time trying to figure out if we can do away with holding the lock, if we can't, it should result in a better comment, possibly add a @todo and issue to have another try in a follow-up, if we can release the lock, then great we don't need to explain why we can't.
Comment #13
catchAlright it looks like that theory was correct.
The remaining race condition was something like as follows:
We lock cache writes, but not cache reads. CacheCollector uses the cache created timestamp (which is stored with millisecond precision) to detect if the cache item it read at the beginning of the request is the same as the one it reads again at the end of the request.
Because we only lock writes, it's possible for two processes to lock acquire, cache write, lock release within the same millisecond. For the usual CacheCollector case this is a non-issue, because we are generally caching things which are expensive to build and don't get written to as such (like the runtime theme registry), so all these things happening in a single millisecond does not really come up. It's a problem here because state is cheap to build and also gets individual keys written to, especially with the database on a ramdisk on the same server etc. like gitlab pipelines.
If State::set() writes a cache item in the same millisecond as another cache item is set, this breaks the race condition detection in ::updateCache(). Another request can read the newly outdated cache item, get to the end of the request, compare with the State::set() one, see them as 'the same' and then write back stale data.
Holding the lock longer was preventing the situation where another process writes back to the cache item immediately after State::set() has written to it, having read a different cache item created in the same millisecond as the call to State::set().
Instead of that, we can usleep() for 10 milliseconds before and after writing, and try a bit harder to acquire the lock, this still results in 2500 passes for the test, but it doesn't involve either the full lock->wait() call or holding the lock that were necessary in earlier iterations.
There are still other theoretical race conditions here - e.g. if two servers have a time offset, and write cache items in the 'same' millisecond even though they are actually different due to the offset. We could potentially protect against this in CacheCollector by hashing the cache data every time it's read, and then comparing the hash when we write back to the cache - but this would introduce hashing overhead to every cache collector read which is not great. Or maybe we could store the hash with the cache item and compare that, which would mean changing the cache data structure a bit, but that might work and allow us to remove the usleep() calls then.
Going to try that approach in the sandbox MR and see if it's straightforward enough.
Comment #14
spokje*cough*
Testbot really doesn't like it...
*cough*
Comment #15
catchOK trying to use a hash of the data doesn't work, or at least not yet, it also requires changing the structure of the cache item which means that even if did work it would probably be minor only.
I'll open a follow-up for that.
https://git.drupalcode.org/project/drupal/-/merge_requests/10704 is the MR and approach to review.
Restored the 2500 runs to its previous state before that diversion, to get a fresh green run on there.
https://git.drupalcode.org/project/drupal/-/jobs/3831420
Comment #17
catchHere's the follow-up: #3496328: Use a hash of the cache data for CacheCollector race condition detection.
Comment #19
catchJust kicked off a third run of https://git.drupalcode.org/project/drupal/-/merge_requests/10705/pipelines which will give us 7500 passing tests in a row assuming it also comes back green.
Comment #20
spokjeHating to be that guy, but hey, I _am_ that guy...
I'm very doubtful if 7500 runs will fit into the current cutoff time of 1 hour.
Comment #21
catch@spokje it's 2500 runs * 3, should be OK.
Comment #22
catchWell that's interesting, 2 runs out of 7500 failed https://git.drupalcode.org/project/drupal/-/jobs/3831692/viewer
Looks like this happened when the overall time for the test went up to 30-50+ seconds from 6-10s
Might be a sign of the lock failing to be acquired or similar.
Don't think this necessarily invalidates the fix here since it's still a significant improvement, but might show we need to lock wait for longer and/or explore the hash idea further in the other issue.
Comment #23
catchPass rate of 12248/12500 now.
Comment #24
catchAnother 1/2500 fail on https://git.drupalcode.org/project/drupal/-/jobs/3832573/viewer so at 14997/15000 passes now.
Comment #29
catchHmm four fails on https://git.drupalcode.org/project/drupal/-/jobs/3849058 out of 2500
Comment #30
catchFamous last words because we're down to 4/15000 odds so false negatives are increasingly likely, but might have figured out the last race condition.
In earlier iterations of the MR, I was holding the lock acquired in State:set() and not releasing it, this was out of paranoia so changed it to release after it was noted in review. I now think there was a good reason for it after all.
In CacheCollector::updateCache(), we use a new cache item as a tombstone record for the following case:
1. Process A - cold cache
2. Process B - invalidates the cache (e.g. a state write).
3. Process A writes the cache back at end of request, with stale data from before process B wrote its data.
To avoid this, Process B writes a new cache item, and then process A sees that a brand new cache item has been written when none existed before, and throws its data away instead.
The remaining race condition looked like this:
1. Process A - cold cache
2. Process B - state write and cache write.
3. Process B - end of request - deletes cache item (fails to acquire a lock or any other reason
4. Process A - because the cache item is deleted, doesn't see it, so writes the stale data to the cache.
Holding the lock does the following:
1. Process A - cold cache
2. Process B - state write and cache write
3. Process B - end of request - still has the lock, so writes to cache again instead of deleting.
4. Process A - finds the new cache item so throws its data away
Comment #31
penyaskitoIf this helps, as someone completely new to this issue, the code comments look as clear as I think they can be to understand the complex issue and the fix.
Comment #32
catch20,000 passes and 0 fails holding the lock, compared to 3/15000 fails without it.
This is probably not enough to prove a negative, but I think it is getting close.
https://git.drupalcode.org/project/drupal/-/jobs/3850861
https://git.drupalcode.org/project/drupal/-/jobs/3850954
https://git.drupalcode.org/project/drupal/-/jobs/3851133
https://git.drupalcode.org/project/drupal/-/jobs/3851201
https://git.drupalcode.org/project/drupal/-/jobs/3851350
https://git.drupalcode.org/project/drupal/-/jobs/3851437
https://git.drupalcode.org/project/drupal/-/jobs/3853860
https://git.drupalcode.org/project/drupal/-/jobs/3854001
edit: 22500 https://git.drupalcode.org/project/drupal/-/pipelines/382543
Comment #33
nicxvan commentedI think this is getting pretty close do we want a branch now with just the cache changes?
I'm trying to figure out how to check the probabilities, it's been a while since I took statistics.
Comment #34
nicxvan commentedOk using Poisson's distribution, then the odds that something that happens 3/15000, having that same thing happen 0 times in 22500 is 1.1%
https://www.wolframalpha.com/input?i2d=true&i=Divide%5B%5C%2840%29Power%...
Now this isn't perfect because it assumes that the 3/15000 rate is constant and obviously we're affecting the thing driving it, but we can't really do better that I can see.
Just for others following along the formula is (e^(-λ) (λ^x))/x!
e is euler's number
λ is the rate 3 divided by 15000 multiplied by 22500 = 4.5
x is the number expected which in this case is 0
Plug those numbers in and you get 1.1%
I interpret that to mean if the rate was 3/15000 then we'd have to run a set of 22500 tests 100 times to get a run that had 0 failures.
That feels definitive to me.
Comment #35
spokjeJust my EUR0.02: I can see an, almost always considerable, rise in the failure rate when it's more busy on Drupal CI.
All runs here have taken place in the Christmas/New Years period, in which it's very quiet around here.
If possible, I would like to see a few runs from monday January 6th onward, when normal business is restored and see if that affects the numbers drastically.
If that would be too long of a wait, Im OK with this as-is, if stuff starts breaking down (more) on Monday we can act then as needed.
Comment #36
catchCouple of general points, mostly answering questions I asked myself while working on this:
This problem is fairly unique to state's use of cache collector. Most cache collector implementations just build a 'runtime' cache from a discovery cache (smaller array from a massive array), so they are not changing values in the underlying data at all.
The most likely problem this change could cause we would be finding badly behaving contrib or custom code that is writing to state every request or something, but that would already cause problems with state caching, the lock wait would make those problems a bit worse, but writing to state every request would be pretty extreme.
With a normal/occasional state write, while the lock is being held, other processes will need to do some extra key/value queries, and won't be able to write back to the cache, but this is relatively cheap (about the same as before we had state caching in the first place). So that bit seems fine.
While adding all the logic, I wondered if state should instead re-implement the parts of cache collector it needs instead of making it more complicated, but:
- overall we'd probably end up with the same amount of complexity, but in two places instead of one
- the changes to cache collector make it more robust for contrib/custom use-cases doing similar things
- even if state moved to 'immediate write' with no end of request lazy-writing at all, it would still have to lock both state writes and cache writes.
There is one possible optimization we can do here which I realised writing the above:When we write to the cache item in State::set(), if we've acquired a lock, include the other state keys that are already loaded too, that will make it more likely that subsequent requests get a cache hit (including while the lock is being held). Not going to have time to work on that for at least a few hours though.
No we can't do that, because we'd run into the same race condition again in another place - even if we acquire a lock, we don't know what happened immediately before we acquired the lock, so we'd need to do all the same checks on whether the cache item we had at the beginning of the request has changed in the meantime etc. in State::set() too. Maybe if we could factor out some of the logic in updateCache() to re-use, but not easy. Follow-up material, but not sure I want to open it even.
@nicxvan https://git.drupalcode.org/project/drupal/-/merge_requests/10704 is the committable/reviewable branch, it should be up-to-date.
Comment #37
catch@nicxvan thanks for the stats research, that's pretty encouraging.
I've pushed a revert of #3477586: [random test failure] LayoutBuilderBlocksTest::testBlockPlaceholder failing to the 2500 test runs branch (https://git.drupalcode.org/project/drupal/-/merge_requests/10749) so that it's possible to keep hitting the 'run pipelines' button on there a bit more next week if this isn't committed before then. That will also give us another 2500 runs in the meantime.
Comment #38
catchLittle trip down memory lane:
#20114: race condition in variable_set in bootstrap.inc and #973436: Overzealous locking in variable_initialize() amongst others.
Comment #39
wim leersArrived here via #3496405: [random test failure] EditorSecurityTest::testEditorXssFilterOverride.
@catch's #13 + #30 + #36 + #38 are 🤯
@nicxvan: it's been >10 years since I saw somebody in a software engineering context mention Poisson's distribution and it reminds me of one of the very few books I still have lying around from studying Computer Science: https://www.taylorfrancis.com/books/mono/10.1201/9781420011425/probabili... 🤓😅
@spokje in #35: +1 for increased CI failure rates when CI infra is under load.
Comment #40
catchSince it's Monday, started kicking off pipelines again. Will edit this comment to add them when I remember to kick another one off (insert xkcd compiling image).
https://git.drupalcode.org/project/drupal/-/pipelines/387144
https://git.drupalcode.org/project/drupal/-/pipelines/387190
Comment #41
catchI think we can skip holding the lock after all.
Short explanation of why:
Performance tests were failing in the latest MR, this is because the cli was holding the lock and preventing the site-under-test from acquiring it (annoying).
Trying to workaround this made me realise we're mainly holding the lock to avoid ::updateCache() throwing away the tombstone record when it can't acquire a lock.
Instead of that, if we've acquired a lock, we can completely disable the end of request logic in state, and release the lock. That means as soon as the state item is written other requests can start to build the cache again, without any interference from the ::set() logic.
Obviously, now need to check that this still fixes the race condition so another 10k+ test runs to kick off on the test-only MR - will start that once the main fix MR is green again.
Comment #42
catchImplemented that. One functional js test failure which I think is unrelated. Updated the test only MR, first 2500 runs came back green, will kick off more over the next 24 hours.
https://git.drupalcode.org/project/drupal/-/pipelines/387799
https://git.drupalcode.org/project/drupal/-/pipelines/387820
https://git.drupalcode.org/project/drupal/-/pipelines/387832
https://git.drupalcode.org/project/drupal/-/pipelines/387846
https://git.drupalcode.org/project/drupal/-/pipelines/387861
https://git.drupalcode.org/project/drupal/-/pipelines/387885
Comment #43
catchToo much status change.
Comment #44
catch15k runs without a failure so far, which is pretty encouraging, so I think this genuinely is ready for review again now. I'll kick off some more pipelines tomorrow.
Comment #45
spokjeGreat work, I suppose this also takes care of random failures in tests that actually need to use State for testing like
\Drupal\Tests\system\Functional\System\CronRunTest::testCronUI?This is an example where we can't just switch to KeyValue, since State is an integral part of running cron.
Comment #46
catch@Spokje yes it should fix cases like that as well.
Comment #47
catchSeveral fails on: https://git.drupalcode.org/project/drupal/-/jobs/3923493
These all happened in a cluster of tests where the runtime doubled.
Comment #48
catchTheory on the above - the runtime doubling could be not acquiring a lock, and having to wait for a second - the runtime doubled when the test itself was holding the lock for the duration of the test (although that prevented any new caching at all so fixed the race condition).
So... trying to be more aggressive when we fail to acquire the lock.
Comment #49
catchUnrelated fail on https://git.drupalcode.org/project/drupal/-/jobs/3925298, otherwise green.
runs:
https://git.drupalcode.org/project/drupal/-/pipelines/388431
https://git.drupalcode.org/project/drupal/-/pipelines/388538
Comment #50
catchThe simplification led to a behaviour change for the path alias cache, but it's very small and still works.
I think it is probably possible to make updateCache() work for what State:::set() wants to do, but I've already spent a lot of time on this issue and need a break, so I think we should defer that to a follow-up - it might reduce some code and make things hopefully even more robust, but it will be as tricky as this issue to ensure we've caught all the cases. Would rather have things correct which hopefully the current state of the MR gets us to.
Because a few things have changed again, going to run more pipelines..
https://git.drupalcode.org/project/drupal/-/pipelines/390206
https://git.drupalcode.org/project/drupal/-/pipelines/390279
https://git.drupalcode.org/project/drupal/-/pipelines/390291
https://git.drupalcode.org/project/drupal/-/pipelines/390300
https://git.drupalcode.org/project/drupal/-/pipelines/391267 <- failures in this one
Comment #51
catchOpened #3498317: Try to make State::set() logic re-usable, directly within CacheCollector::updateCache().
Comment #53
dww(Sorry for the assigned/unassigned noise -- accidentally clicked something in the MR review UI).
Resolved the opened threads, since @catch has already addressed all the feedback.
Opened a few trivial suggestions for some nits, and to rename a test method that no longer does what the old name says.
I would RTBC, but I think the suggestions are worth applying, first. 😅
Initial pass at saving credit:
@catch (duh)
@alexpott for MR reviews + suggestions
@Spokje, @nicxvan, @godotislate, @luke.leber, and myself for MR and issue reviews
Almost there!
Thanks,
-Derek
p.s. restoring the Bug Smash Initiative tag, which I think @catch accidentally clobbered in #13 with a stale form submit.
Comment #54
catchTracked down the very specific race condition with the test (well I think I did) which was still resulting in some failures occasionally - about 1/2500.
I think it is:
1.test requests a page
2. page writes to the state cache collector
3. test sets state
4. test requests a page.
Because the test doesn't use WaitTerminateTestTrait, if #2 is holding the lock while #3 happens, we go into the !$lock_acquired branch in state.
But the implementation so far was concentrating on the request that hits the $lock_acquired branch eventually winning out when it reaches the end of the request (more or less eventual consistency). In the test, there is no 'end of the request', there is just the end of the test, and the page we hit immediately after trying to set state can still get the wrong value in the meantime. The answer is to even more aggressively write to the cache when we can't acquire the lock. It still doesn't work if the underlying request takes longer than a second to complete, because then both the pre- and post- lock acquire attempt cache writes can still be overwritten. We could wait longer than a second, but on a real request the end of request logic is more likely to kick in later and clean everything up.
Bit annoyed that I didn't look at the test in more detail earlier because that's obvious in retrospect, but was trying to focus on the stampede condition that real sites will hit vs functional test weirdness.
Fresh set of runs:
https://git.drupalcode.org/project/drupal/-/pipelines/392873
https://git.drupalcode.org/project/drupal/-/pipelines/393001
https://git.drupalcode.org/project/drupal/-/jobs/3985642 <- 1 fail
Comment #55
godotislateIs "Needs work" correct as current status, or is this ready to be reviewed again?
Comment #56
catch@godotislate it's not actually fixing the race condition at the moment so very much needs work - although review of the approach would probably still be useful.
Comment #57
catchMerged 11.x into the repeat test run MR. One fail in 5000 runs.
https://git.drupalcode.org/project/drupal/-/jobs/4331449
https://git.drupalcode.org/project/drupal/-/jobs/4331562 -> fail
I'm wondering if the remaining test failure is due to #2347867: Race conditions with lock/cache, session storage - add a non-transactional database connection (e.g. a deadlock on the cache/lock table), and if so, that will be unfixable with this MR. Given that, going to rebase the main MR here and move this back to needs review.
Comment #58
bgustafson commentedI have been having issues where state is being reset somehow/somewhere, which seems to be a caching issue. I'm planning to opt out of state cache (as per https://www.drupal.org/node/3443018) until this is resolved. Can someone clarify if this and/or #3496328: Use a hash of the cache data for CacheCollector race condition detection will be resolved before the Drupal 10 EOL? It seems like a major issue to me (I see this is already marked as Critical).
Comment #60
nicxvan commentedJust found this again, I think this would be a great one to get in.
A bunch of conflicts though, so I'm not sure it's worth reviewing in the current state (lol).
Comment #61
catchRebased.
Comment #62
catchAdded some unit test coverage for both cache collector and state for everything I can think of.
To some extent this is 'testing the implementation' but to be entirely honest I don't have any other ideas there at the moment.
Comment #63
catchJsonApiPerformanceTest had a new failure. Debugged it and it was due to the new stricter checks in CacheCollector as to whether we should write a cache item or not. Additional sleep() calls after the cache warming requests fixed it without changing any test assumptions.
Comment #64
nicxvan commentedI think this is pretty much ready.
There is a new conflict, and we should decide if 35 deserves another large run (is drupalcon usually busier for CI or lighter?)
Comment #65
catchRebased. The unit test now sets up stubs, whereas for the added method we actually want mocks, so I moved several things inline in the method.
If we want to do a large run for #35 we'll need to recreate (probably easier than rebase) https://git.drupalcode.org/project/drupal/-/merge_requests/10749 which restores a state usage in a test that was failing randomly then runs it 2500 times.
Comment #66
nicxvan commentedOn second thought I don't think we really need the 2500 run, we've since fixed those issues and this already demonstrated it fixed one of those so this is just an outright improvement.
Comment #68
godotislateOpen comment on the MR.
Comment #69
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #70
catchDon't think it's failing, rebased in case the bot is picking up a stale branch.
Comment #71
godotislateCouple Qs on the MR about the comment content.
Comment #72
godotislateI think this lgtm now.
Comment #73
alexpottLeft a couple of comments on the code that need addressing.
Comment #74
catchRemoved the cruft, added an additional unit test method, and updated the usleep() bit (to also try to simplify it).
Comment #75
berdirWent through this as well, looks good to me, my minor feedback was adressed and I confirmed that the review from @alexpott was adressed.
I may kind of regret pushing for the state collector cache after reading State::setMultiple(). It's useful, but that complexity is quite a steep price we have to pay for that.
Comment #76
catchIf this one fixes it, or about 99.9% which seems to be the case, then I won't regret it. We're talking about billions, possibly trillions of database queries saved across all Drupal sites, state is right in the middle of the critical path. But yes it is gnarly in there.
Comment #77
alexpottWe should backport this to 11.x and 11.3.x right?
Comment #79
alexpottCommitted 8d660fe and pushed to main. Thanks!
Comment #81
catchPushed an 11.x backport branch/MR.
If this is a clean cherry-pick to 11.3.x from 11.x we could do that, but if not I'd leave it in 11.4 - only known case of anyone running into this is core test runs, it should be extremely unlikely in the wild.
Comment #82
catchRebase is passing except for a parse error in AssetResolverTest on PHP 8.3 which I think is in HEAD, definitely not changed here.
Comment #83
godotislateThink the 11.x issue with AssetResolverTest was reverted in #3366561: Support preloading of fonts in the libraries API.
Comment #85
catchReverted the main commit - HelpTopicsSyntaxTest started taking 20 minutes, it sets state a lot.
Comment #86
catch#3586606: Improve performance of HelpTopicsSyntaxTest is in main so this should be OK again.
Comment #87
godotislateAt least this one's not 20 minutes anymore.
Before we go ahead again, should we check to see if the MR build had any other noticeably slow tests?
Comment #88
alexpottThere is some indication that the MR is slower if we compare PHP Functional 6/8....
MR
https://git.drupalcode.org/project/drupal/-/jobs/9585741
Another MR on the same runner
https://git.drupalcode.org/project/drupal/-/jobs/9591086
HEAD
https://git.drupalcode.org/project/drupal/-/jobs/9589095
But I'm not really sure what this is telling us - there was no test that took 20 mins :)
Comment #89
catch#3506148: [meta] Replace all uses of state in tests with direct key value should eventually remove all state usage for controlling, um, state in tests. That unfortunately has a high chance of hitting a lock wait because both the test and the site under test will end up trying to write to the cache, via an explicit state set, often multiple times per test method because different state values keep getting set. Exactly how widespread a problem this is likely to be I don't know though. We opened that issue and this one partly due to a high rate of random test failures due to this, not sure how many tests that we didn't already switch to key/value actually fail due to the race condition though at this point.
Kicked off another pipeline to another run to compare against.
Comment #90
catchFunctional 6/8 still a bit slower than the other examples but not as much, very hard to tell if it's the MR or just usual pipeline variation.
https://git.drupalcode.org/project/drupal/-/jobs/9593982
However there's a new performance test fail - probably needs better setup
Comment #91
longwaveI found the $write_cache logic a bit hard to follow here so I've tried to simplify it, hopefully this makes more sense to others too?
Comment #92
catchhttps://git.drupalcode.org/project/drupal/-/merge_requests/10704/diffs?c... looks good to me visually, however the performance fails suggest it's resulting in different behaviour.
Comment #93
catchOn the other hand there's a green run on #3496328: Use a hash of the cache data for CacheCollector race condition detection so maybe we should merge that issue back into this one? I originally spun it out to make this issue not too much change at once, but not sure it's actually any more or less change really given the complexity here.
Comment #94
longwaveI just rebased 3496257-race-conditionsbad-lock to see if that helps, because there were 71 commits here and it was quite far behind main, so possibly something odd going on with merging/CI.
Comment #95
longwaveAlso wondering if those sleeps that you added here still aren't enough, maybe this is some non-determinism thing to do with the order things can happen in during tests...
Comment #96
longwaveHm, so the results are exactly the same after rebase, investigating...
Comment #97
longwaveAs far as I can tell: the numbers are now truly correct, the fix and reduction is real: in main we overwrite the cached data with possibly stale data, in this MR we notice that the item is newer and we don't overwrite it.
Comment #99
longwave@catch MR!15935 proves it, that's a revert of my simplification work leaving only the previous MR behind, so something else has changed somewhere since this was previously green for this to now break HEAD!
Comment #100
longwaveWondering if these changes are a side effect of #3503843: Remove automatic preloading of all "public" routes, cache routes in fast chained bin or something similar?
Comment #101
catchOK interesting, well that proves that the behaviour is equivalent, although it doesn't explain why #3496328: Use a hash of the cache data for CacheCollector race condition detection doesn't require the same changes.
Comment #102
longwaveOh, because we check the hash in #3496328: Use a hash of the cache data for CacheCollector race condition detection then we don't do any writes if the cache item didn't actually change, but here we blindly write anyway if the cache data is identical. So maybe we just land that...? However, the hashing isn't entirely free...
Comment #103
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. The merge request has merge conflicts and cannot be merged. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #104
catchI think we should go ahead with #3496328: Use a hash of the cache data for CacheCollector race condition detection which includes all the changes here + additional refactoring on top, so marking this postponed - we can close it once that's in.