From f86c1a87f9b17b5b8ca8b9038eff277aeaa28327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Espi=C3=A9?= Date: Tue, 3 Oct 2017 13:53:53 +0000 Subject: [PATCH] N.1065 Fix performance issues. Limit APC emulation cache entries to avoid disk saturation. New configuration entry added: 'apc_cache_emulation.max_entries'. SVN:trunk[4960] --- core/apc-emulation.php | 78 +++++++++++++++++++++++++++++---------- core/config.class.inc.php | 8 ++++ 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/core/apc-emulation.php b/core/apc-emulation.php index 409b16ea4..27e8fcc20 100644 --- a/core/apc-emulation.php +++ b/core/apc-emulation.php @@ -67,7 +67,7 @@ function apc_emul_store_unit($sKey, $value, $iTTL) } } $bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false); - apc_emul_manage_ttl(); + apc_emul_manage_new_entry($sFilename); return $bRes; } @@ -89,6 +89,10 @@ function apc_fetch($key) return apc_emul_fetch_unit($key); } +/** + * @param $sKey + * @return bool|mixed + */ function apc_emul_fetch_unit($sKey) { // Try the 'TTLed' version @@ -175,22 +179,53 @@ function apc_emul_get_cache_filename($sKey) } -function apc_emul_manage_ttl() +/** Manage the cache files when a new cache entry is added + * @param string $sNewFilename new cache file added + */ +function apc_emul_manage_new_entry($sNewFilename) { // Check only once per request - static $bAlreadyChecked = false; - if ($bAlreadyChecked) + static $aFilesByTime = null; + static $iFileCount = 0; + $iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries'); + if (!$aFilesByTime) { - return; + $sRootCacheDir = apc_emul_get_cache_filename(''); + $aFilesByTime = apc_emul_list_files_time($sRootCacheDir); + $iFileCount = count($aFilesByTime); + if ($iMaxFiles !== 0) + { + asort($aFilesByTime); + } + } + else + { + $aFilesByTime[$sNewFilename] = time(); + $iFileCount++; + } + if (($iMaxFiles !== 0) && ($iFileCount > $iMaxFiles)) + { + $iFileNbToRemove = $iFileCount - $iMaxFiles; + foreach($aFilesByTime as $sFileToRemove => $iTime) + { + @unlink($sFileToRemove); + if ($iFileNbToRemove-- === 0) + { + break; + } + } + $aFilesByTime = array_slice($aFilesByTime, $iFileCount - $iMaxFiles, null, true); + $iFileCount = $iMaxFiles; } - $sRootCacheDir = apc_emul_get_cache_filename(''); - apc_emul_manage_ttl_dir($sRootCacheDir); - $bAlreadyChecked = true; } -function apc_emul_manage_ttl_dir($sCheck) +/** Get the list of files with their associated access time + * @param string $sCheck Directory to scan + * @param array $aFilesByTime used by recursion + * @return array + */ +function apc_emul_list_files_time($sCheck, &$aFilesByTime = array()) { - $iTTL = MetaModel::GetConfig()->Get('apc_cache.query_ttl'); // Garbage collection $aFiles = array_diff(@scandir($sCheck), array('.', '..')); foreach($aFiles as $sFile) @@ -198,24 +233,29 @@ function apc_emul_manage_ttl_dir($sCheck) $sSubFile = $sCheck.'/'.$sFile; if (is_dir($sSubFile)) { - apc_emul_manage_ttl_dir($sSubFile); + apc_emul_list_files_time($sSubFile, $aFilesByTime); } else { - apc_emul_check_ttl_file($sSubFile, $iTTL); + $iTime = apc_emul_get_file_time($sSubFile); + if ($iTime !== false) + { + $aFilesByTime[$sSubFile] = $iTime; + } } } + return $aFilesByTime; } -function apc_emul_check_ttl_file($sFilename, $iTTL) +/** Get the file access time if TTL is managed + * @param string $sFilename + * @return bool|int returns the file atime or false if not relevant + */ +function apc_emul_get_file_time($sFilename) { - $iCurTime = time(); if (strpos(basename($sFilename), '-') === 0) { - $iLimitTime = @fileatime($sFilename) + $iTTL + rand(-$iTTL / 10, $iTTL / 10); - if ($iLimitTime < $iCurTime) - { - @unlink($sFilename); - } + return @fileatime($sFilename); } + return false; } diff --git a/core/config.class.inc.php b/core/config.class.inc.php index e83c03d4e..0ca41f608 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -436,6 +436,14 @@ class Config 'source_of_value' => '', 'show_in_conf_sample' => true, ), + 'apc_cache_emulation.max_entries' => array( + 'type' => 'integer', + 'description' => 'Maximum number of cache entries (0 means no limit)', + 'default' => 1000, + 'value' => 1000, + 'source_of_value' => '', + 'show_in_conf_sample' => false, + ), 'timezone' => array( 'type' => 'string', 'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP',