Skip to content

Fix database deadlock exception in AssetService::getTypoScript #1945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: development
Choose a base branch
from

Conversation

adamkoppede
Copy link
Contributor

If the database caching backend is used for the "vhs_main" cache, database deadlocks occur if there are many concurrent requests to a page with uncached content. A full reproduction example is available on GitHub: https://github.com/adamkoppede/reproduce-deadlock-with-fluidtypo3-vhs-7-1

The deadlock occurs in the EXT:core Typo3DatabaseBackend::set() method. I created an issue in the upstream TYPO3 bug tracker: https://forge.typo3.org/issues/106593

Currently Typo3DatabaseBackend::set() is called four times per HTTP request. The first commit in this change set extends the static::$settingsCache so it contains plugin.tx_vhs fully, instead of just plugin.tx_vhs.settings; this reduces the Typo3DatabaseBackend::set() calls down to one per request. The second commit removes the Typo3DatabaseBackend::set() call for pages with uncached content, as they cannot be served from cache.

With TYPO3 v13, AssetService calls FrontendInterface::set() four times
in AssetService::getTypoScript() for a single AssetService::buildAll()
invocation. The cache usage was introduced in e84af96 ("[BUGFIX] Handle
new TS-not-set limitation on v13 (FluidTYPO3#1928)"), as TYPO3 v13 no longer fully
sets up TypoScript when serving fully-cached pages.

The four callers of AssetService::getTypoScript() are:

- AssetService::getSettings()
- AssetService::writeCachedMergedFileAndReturnTag()
- AssetService::getFileIntegrity()
- AssetService::generateTagForAssetType()

Reducing the number of FrontendInterface::set() calls reduces the amount
of database deadlock errors that occur if the Typo3DatabaseBackend is
used for the "vhs_main" cache and many concurrent requests hit the same
TYPO3 page that contains at least one uncached content element. But it
doesn't solve them completely; e.q. They still occur but less frequent.
This issue is tracked in the upstream TYPO3 bug tracker as #106593 [1].
A reproduction example is available on GitHub [2].

[1]: https://forge.typo3.org/issues/106593
[2]: https://github.com/adamkoppede/reproduce-deadlock-with-fluidtypo3-vhs-7-1
Since change e84af96 ("[BUGFIX] Handle new TS-not-set limitation on v13
(FluidTYPO3#1928)") the "plugin.tx_vhs" section of the TypoScript setup is written
into a cache. This is necessary as TYPO3 v13
PrepareTypoScriptFrontendRendering [1] no longer performs the full
TypoScript setup when serving a fully-cached page. An "Setup array has
not been initialized" error is thrown when trying to access the
uninitialized TypoScript setup.

For pages with uncached content, FrontendInterface::set() is called on
each request. This leads to database deadlock errors if the
Typo3DatabaseBackend is used for the "vhs_main" cache and many
concurrent requests hit the same TYPO3 page. This issues is tracked in
upstream TYPO3 bug tracker as #106593 [2]. A fully reproduction example
is available on GitHub [3].

We don't need to store the TypoScript of those pages with uncached
content in the cache, because they cannot be served from cache and thus
forcing the full TypoScript setup.

[1]: https://github.com/TYPO3/typo3/blob/v13.4.9/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php#L146
[2]: https://forge.typo3.org/issues/106593
[3]: https://github.com/adamkoppede/reproduce-deadlock-with-fluidtypo3-vhs-7-1
@adamkoppede adamkoppede force-pushed the bugfix/database-deadlock-exception-in-AssetService branch from c1a42f5 to 57203d8 Compare April 21, 2025 21:45
@adamkoppede
Copy link
Contributor Author

adamkoppede commented Apr 21, 2025

Tests pass locally with composer update --with typo3fluid/fluid:4.1.0 on my machine. This is the version used in the latest GitHub pipeline for the development branch: https://github.com/FluidTYPO3/vhs/actions/runs/14401105773/job/40386878009

The current development branch (v7.1.1) also fails with composer update --with typo3fluid/fluid:4.1.1 and composer update --with typo3fluid/fluid:4.1.2 locally. The updates contain changes related to condition view helpers: https://github.com/TYPO3/Fluid/releases/tag/4.1.1 / https://github.com/TYPO3/Fluid/releases/tag/4.1.2

The issue seems to affect the unit tests only. The output for the following seems fine in a TYPO3 v13 instance with all three typo3fluid/fluid 4.1.x versions:

<html
	xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
	xmlns:vhs="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers"
	data-namespace-typo3-fluid="true"
>

<ol>
    <li><f:if condition="1 == 1" then="good" else="bad"/></li>
    <li><f:if condition="0 == 1" then="bad" else="good"/></li>
    <li><vhs:condition.context.isProduction then="good" else="bad"/></li>
    <li><vhs:condition.context.isTesting then="bad" else="good"/></li>
    <li>
        <vhs:condition.iterator.contains haystack="['hello', 'world']" needle="world">
            <f:then>good</f:then>
            <f:else>bad</f:else>
        </vhs:condition.iterator.contains>
    </li>
    <li>
        <vhs:condition.iterator.contains
            haystack="['hello', 'world']"
            needle="underworld"
        >
            <f:then>bad</f:then>
            <f:else>good</f:else>
        </vhs:condition.iterator.contains>
    </li>
    <li>
        <vhs:condition.iterator.contains
            haystack="['hello', 'world']"
            needle="world"
            then="good"
        />
    </li>
    <li>
        <vhs:condition.iterator.contains
            haystack="['hello', 'world']"
            needle="world"
            then="good"
            else="bad"
        />
    </li>
    <li>
        <vhs:condition.iterator.contains
            haystack="['hello', 'world']"
            needle="underworld"
            else="good"
        />
    </li>
    <li>
        <vhs:condition.iterator.contains
            haystack="['hello', 'world']"
            needle="underworld"
            then="bad"
            else="good"
        />
    </li>
</ol>

</html>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant