A thread-safe configuration manager extension for TYPO3 CMS that extends the core ConfigurationManager with built-in locking mechanisms to prevent race conditions during configuration read/write operations.
In TYPO3, the ConfigurationManager is responsible for managing system configurations.
However, in multiprocess environments (like web servers), concurrent access to configuration files can lead to race conditions, file corruption, or lost updates if not properly synchronized. This is especially problematic when multiple processes attempt to read from or write to the same configuration file at the same time.
The most obvious problem then is this core exception you might encounter randomly:
Uncaught TYPO3 Exception: TYPO3\CMS\Core\Configuration\ConfigurationManager::getLocalConfiguration(): Return value must be of type array, int returned | TypeError thrown in file /var/www/html/vendor/typo3/cms-core/Classes/Configuration/ConfigurationManager.php in line 121.
- Thread-safe configuration access: Uses TYPO3's built-in locking API to prevent race conditions
- Shared locks for reading: Multiple processes can read configuration simultaneously
- Exclusive locks for writing: Ensures only one process can modify configuration at a time
- Atomic writes: Uses temporary files and atomic moves to prevent corruption
- Configurable timeouts: Adjustable lock timeout with exponential backoff
- Compatible with TYPO3 13.4 LTS and 14.3: Tested against the matching
Bootstrappatches shipped with the extension - Replaces the core ConfigurationManager: Wired in via a small
Bootstrapcore patch (see Installation)
Add the extension to your TYPO3 project:
composer require plan2net/safe-config-managerAs the ConfigurationManager is directly instantiated by TYPO3 through the new keyword (no DI container involvement), you have to patch the core Bootstrap class to use the SafeConfigurationManager instead.
The extension ships ready-to-apply patches for each supported TYPO3 major version:
patches/typo3-cms-core-13-bootstrap-safe-config-manager.patch— TYPO3 13.4 LTSpatches/typo3-cms-core-14-bootstrap-safe-config-manager.patch— TYPO3 14.3
Both wrap the replacement in a class_exists check, so the original ConfigurationManager is still used if the extension is ever disabled.
Use the "cweagans/composer-patches" plugin to apply the patch automatically during composer install. Pick the patch matching your TYPO3 major and reference it from your project's composer.json:
{
"extra": {
"patches": {
"typo3/cms-core": {
"Use SafeConfigurationManager for thread-safe configuration handling": "vendor/plan2net/safe-config-manager/patches/typo3-cms-core-13-bootstrap-safe-config-manager.patch"
}
}
}
}The SafeConfigurationManager uses TYPO3's LockingStrategyInterface to provide:
- Shared locks (
LOCK_CAPABILITY_SHARED) for read operations (getLocalConfiguration()) — multiple readers run in parallel. - Exclusive locks (
LOCK_CAPABILITY_EXCLUSIVE) for write operations (writeLocalConfiguration(),updateLocalConfiguration()) — writers serialize against readers and against each other. - Single exclusive lock around read-modify-write cycles for
setLocalConfigurationValueByPath(),setLocalConfigurationValuesByPathValuePairs(),removeLocalConfigurationKeysByPath(),enableFeature(), anddisableFeature()— without this, two concurrent updates could each read the old configuration and overwrite each other's changes. - Non-blocking acquire with bounded retry —
acquire()is called withLOCK_CAPABILITY_NOBLOCKin an exponential-backoff loop, capped by the configuredlockTimeout.
All write operations use atomic file operations:
- Write to a temporary file
- Perform atomic rename to final location
- Clear opcache for the configuration file
- Clean up temporary files on failure
A single lock identifier is used per configuration file:
safe_config_manager_<md5 of configuration file path>
All operations contend for this one identifier; the SHARED or EXCLUSIVE mode passed to acquire() differentiates intent. Using a separate identifier per operation would put readers and writers on different locks and make the shared/exclusive distinction inert.
Configure the default lock timeout in ext_localconf.php or AdditionalConfiguration.php:
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['safe_config_manager']['lockTimeout'] = 15; // secondsYou can configure which locking strategy to prefer:
// Prefer FileLockStrategy (default: 75)
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locking']['strategies'][\TYPO3\CMS\Core\Locking\FileLockStrategy::class]['priority'] = 100;
// Configure custom lock directory
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locking']['strategies'][\TYPO3\CMS\Core\Locking\FileLockStrategy::class]['lockFileDir'] = 'safe_config_locks';- TYPO3 Version: 13.4 LTS, 14.3
- PHP Version: 8.2+
- Locking Strategies: All TYPO3 core locking strategies supported
- File Systems: Compatible with local and network file systems
If you see LockAcquireException errors:
-
Increase the lock timeout:
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['safe_config_manager']['lockTimeout'] = 30;
-
Check for deadlocks in your configuration update logic
-
Monitor lock file directory permissions and disk space
-
Use appropriate locking strategy for your environment:
- Local filesystems: FileLockStrategy (default)
- Network filesystems: SemaphoreLockStrategy or SimpleLockStrategy
-
Avoid frequent configuration updates in tight loops
-
Use bulk operations (
setLocalConfigurationValuesByPathValuePairs) instead of multiple single updates
GPL-2.0-or-later
plan2net GmbH - https://www.plan2.net