Skip to content

Commit afe6cb8

Browse files
committed
[BUGFIX] Don't cache TypoScript for pages with uncached content
Since change e84af96 ("[BUGFIX] Handle new TS-not-set limitation on v13 (#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 force full TypoScript setup even when answering subsequent requests. [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
1 parent df366c2 commit afe6cb8

1 file changed

Lines changed: 67 additions & 44 deletions

File tree

Classes/Service/AssetService.php

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class AssetService implements SingletonInterface
5050

5151
protected static bool $typoScriptAssetsBuilt = false;
5252
protected static ?array $typoScriptCache = null;
53+
protected static bool $currentlyBuildingCacheable = true;
5354
protected static array $cachedDependencies = [];
5455
protected static bool $cacheCleared = false;
5556

@@ -71,51 +72,60 @@ public function usePageCache(object $caller, bool $shouldUsePageCache): bool
7172

7273
public function buildAll(array $parameters, object $caller, bool $cached = true, ?string &$content = null): void
7374
{
74-
if ($content === null) {
75-
$content = &$caller->content;
75+
$wasBuildingCacheableBefore = static::$currentlyBuildingCacheable;
76+
if ($caller instanceof TypoScriptFrontendController && $caller->isINTincScript()) {
77+
static::$currentlyBuildingCacheable = false;
7678
}
79+
try {
80+
if ($content === null) {
81+
$content = &$caller->content;
82+
}
7783

78-
$settings = $this->getSettings();
79-
$buildTypoScriptAssets = (
80-
!static::$typoScriptAssetsBuilt
81-
&& ($cached || $this->readCacheDisabledInstructionFromContext())
82-
);
83-
if ($buildTypoScriptAssets && isset($settings['asset']) && is_array($settings['asset'])) {
84-
foreach ($settings['asset'] as $name => $typoScriptAsset) {
85-
if (!isset($GLOBALS['VhsAssets'][$name]) && is_array($typoScriptAsset)) {
86-
if (!isset($typoScriptAsset['name'])) {
87-
$typoScriptAsset['name'] = $name;
88-
}
89-
if (isset($typoScriptAsset['dependencies']) && !is_array($typoScriptAsset['dependencies'])) {
90-
$typoScriptAsset['dependencies'] = GeneralUtility::trimExplode(
91-
',',
92-
(string) $typoScriptAsset['dependencies'],
93-
true
94-
);
84+
$settings = $this->getSettings();
85+
$buildTypoScriptAssets = (
86+
!static::$typoScriptAssetsBuilt
87+
&& ($cached || $this->readCacheDisabledInstructionFromContext())
88+
);
89+
if ($buildTypoScriptAssets && isset($settings['asset']) && is_array($settings['asset'])) {
90+
foreach ($settings['asset'] as $name => $typoScriptAsset) {
91+
if (!isset($GLOBALS['VhsAssets'][$name]) && is_array($typoScriptAsset)) {
92+
if (!isset($typoScriptAsset['name'])) {
93+
$typoScriptAsset['name'] = $name;
94+
}
95+
if (isset($typoScriptAsset['dependencies']) && !is_array($typoScriptAsset['dependencies'])) {
96+
$typoScriptAsset['dependencies'] = GeneralUtility::trimExplode(
97+
',',
98+
(string) $typoScriptAsset['dependencies'],
99+
true
100+
);
101+
}
102+
Asset::createFromSettings($typoScriptAsset);
95103
}
96-
Asset::createFromSettings($typoScriptAsset);
97104
}
105+
static::$typoScriptAssetsBuilt = true;
98106
}
99-
static::$typoScriptAssetsBuilt = true;
100-
}
101-
if (empty($GLOBALS['VhsAssets']) || !is_array($GLOBALS['VhsAssets'])) {
102-
return;
103-
}
104-
$assets = $GLOBALS['VhsAssets'];
105-
$assets = $this->sortAssetsByDependency($assets);
106-
$assets = $this->manipulateAssetsByTypoScriptSettings($assets);
107-
$buildDebugRequested = (isset($settings['asset']['debugBuild']) && $settings['asset']['debugBuild'] > 0);
108-
$assetDebugRequested = (isset($settings['asset']['debug']) && $settings['asset']['debug'] > 0);
109-
$useDebugUtility = (isset($settings['asset']['useDebugUtility']) && $settings['asset']['useDebugUtility'] > 0)
110-
|| !isset($settings['asset']['useDebugUtility']);
111-
if ($buildDebugRequested || $assetDebugRequested) {
112-
if ($useDebugUtility) {
113-
DebuggerUtility::var_dump($assets);
114-
} else {
115-
echo var_export($assets, true);
107+
if (empty($GLOBALS['VhsAssets']) || !is_array($GLOBALS['VhsAssets'])) {
108+
return;
109+
}
110+
$assets = $GLOBALS['VhsAssets'];
111+
$assets = $this->sortAssetsByDependency($assets);
112+
$assets = $this->manipulateAssetsByTypoScriptSettings($assets);
113+
$buildDebugRequested = (isset($settings['asset']['debugBuild']) && $settings['asset']['debugBuild'] > 0);
114+
$assetDebugRequested = (isset($settings['asset']['debug']) && $settings['asset']['debug'] > 0);
115+
$useDebugUtility =
116+
(isset($settings['asset']['useDebugUtility']) && $settings['asset']['useDebugUtility'] > 0)
117+
|| !isset($settings['asset']['useDebugUtility']);
118+
if ($buildDebugRequested || $assetDebugRequested) {
119+
if ($useDebugUtility) {
120+
DebuggerUtility::var_dump($assets);
121+
} else {
122+
echo var_export($assets, true);
123+
}
116124
}
125+
$this->placeAssetsInHeaderAndFooter($assets, $cached, $content);
126+
} finally {
127+
static::$currentlyBuildingCacheable = $wasBuildingCacheableBefore;
117128
}
118-
$this->placeAssetsInHeaderAndFooter($assets, $cached, $content);
119129
}
120130

121131
public function buildAllUncached(array $parameters, object $caller, ?string &$content = null): void
@@ -154,23 +164,35 @@ protected function getTypoScript(): array
154164
return static::$typoScriptCache;
155165
}
156166

157-
$cache = $this->cacheManager->getCache('vhs_main');
158-
$pageUid = $this->readPageUidFromContext();
159-
$cacheId = 'vhs_asset_ts_' . $pageUid;
160-
$cacheTag = 'pageId_' . $pageUid;
161-
162167
try {
163168
$allTypoScript = $this->configurationManager->getConfiguration(
164169
ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT
165170
);
166171
$typoScript = GeneralUtility::removeDotsFromTS($allTypoScript['plugin.']['tx_vhs.'] ?? []);
167-
$cache->set($cacheId, $typoScript, [$cacheTag]);
172+
173+
// If we are rendering cached content we need to persist the
174+
// TypoScript for future requests that should be answered from
175+
// cache. Newer TYPO3 versions no longer load TypoScript when
176+
// answering requests from cache, triggering the \RuntimeException
177+
// "Setup array has not been initialized" above.
178+
if (static::$currentlyBuildingCacheable) {
179+
$pageUid = $this->readPageUidFromContext();
180+
$cache = $this->cacheManager->getCache('vhs_main');
181+
$cacheId = 'vhs_asset_ts_' . $pageUid;
182+
$cacheTag = 'pageId_' . $pageUid;
183+
$cache->set($cacheId, $typoScript, [$cacheTag]);
184+
}
168185
} catch (\RuntimeException $exception) {
169186
if ($exception->getCode() !== 1666513645) {
170187
// Re-throw, but only if the exception is not the specific "Setup array has not been initialized" one.
171188
throw $exception;
172189
}
173190

191+
$pageUid = $this->readPageUidFromContext();
192+
$cache = $this->cacheManager->getCache('vhs_main');
193+
$cacheId = 'vhs_asset_ts_' . $pageUid;
194+
$cacheTag = 'pageId_' . $pageUid;
195+
174196
// Note: this case will only ever be entered on TYPO3v13 and above. Earlier versions will consistently
175197
// produce the necessary TS array from ConfigurationManager - and will not raise the specified exception.
176198

@@ -181,6 +203,7 @@ protected function getTypoScript(): array
181203
/** @var array|false $fromCache */
182204
$fromCache = $cache->get($cacheId);
183205
if (is_array($fromCache)) {
206+
static::$typoScriptCache = $fromCache;
184207
return $fromCache;
185208
}
186209

0 commit comments

Comments
 (0)