[UPDATE] This issue is still reproducible on the current master (8.6.0-dev)
Description of symptoms:
One of our customers runs a MySQL replication based on the binary logs of MySQL. One day the growth of the binary logs became so immense that the file system partition eventually exceed its capacity and the production website went offline with a lot of different errors. (We're talking about 10 GB growth per day with 5 days of hold-back time on a 50 GB partition for all MySQL related stuff. The whole TYPO3 DB itself is just about 1 GB in size)
Technical Problem:
The class \TYPO3\CMS\Extbase\Reflection\ReflectionService
rewrites all ReflectionData_* entries from the table cf_extbase_reflection which were read during rendering of the page, regardless if they were modified or not.
As Leendert van Beelen pointed out this happens when the property $dataCacheNeedsUpdate
is set to true. This occurs every time \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper::registerRenderMethodArguments
is called (which seems to happen each time a ViewHelper is beeing initialized) since the method will call \TYPO3\CMS\Extbase\Reflection\ReflectionService::getMethodParameters
and \TYPO3\CMS\Extbase\Reflection\ReflectionService::getMethodTagsValues
on the uninitialized ReflectionService.
The results of the reflection are, however, not stored in any cache and will therefore be called on each request.
How to (theoretically) solve this:
I've thought about three different ways which might solve this problem in an accurate manner:
1. Do not make ReflectionService a Singleton (as pointed out by Leendert van Beelen)
2. Do not rely on ReflectionService in AbstractViewHelper::registerRenderMethodArguments, since the results aren't stored anyway.
3. Proper initialization of the ReflectionService in AbstractViewHelper with setting of dataCache (so the reflection results will be stored)
I think of the first option as the best one for a couple of reasons:
a) setDataCache(...) must be called to make this thing work properly
b) initialize() "should" be called for each instance, too
c) the cacheIdentifier is stored in the object, but is not valid for the whole request but only the "currently rendered extbase plugin".
d) the cached data shrinks in size because not every reflection information which might have been set before the object was initialized will be saved
e) shutdown()/saveToCache() will be bound to the cacheIdentifier. ReflectionData will only be updated if [data of this specific identifier] was touched. Any other untouched cache entrie will not be unnecessarily re-persisted.
A fourth option might be to ensure the ReflectionService can't be used improperly, but this would require an API change.
How to reproduce the Problem:
1. Clone TYPO3 dev-master and install it.
2. Require any extbase extension (like News, but it doesn't really matter. It just needs to have a Frontend Plugin)
3. Create one Page with a TypoScript Template. Include fluid_styled_content and change page.10
setup to: page.10 < styles.content.get
4. Insert an extbase plugin on the page. Show it in the frontend.
5. Reload the page multiple times and watch the UID of the ReflectionData_News (in this case) increment. Each increment of the UID means this cache entry was rewritten.
Some more information:
Sometimes the UID increases by 2 or more. This might happen if there are multiple extbase plugins of the same extension used on one page (Happened on our customers page)