diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index aa288301381b7..d7d47680d3e89 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -131,6 +131,7 @@ 'OCA\\Settings\\SetupChecks\\PhpOpcacheSetup' => $baseDir . '/../lib/SetupChecks/PhpOpcacheSetup.php', 'OCA\\Settings\\SetupChecks\\PhpOutdated' => $baseDir . '/../lib/SetupChecks/PhpOutdated.php', 'OCA\\Settings\\SetupChecks\\PhpOutputBuffering' => $baseDir . '/../lib/SetupChecks/PhpOutputBuffering.php', + 'OCA\\Settings\\SetupChecks\\PhpSAPI' => $baseDir . '/../lib/SetupChecks/PhpSAPI.php', 'OCA\\Settings\\SetupChecks\\PushService' => $baseDir . '/../lib/SetupChecks/PushService.php', 'OCA\\Settings\\SetupChecks\\RandomnessSecure' => $baseDir . '/../lib/SetupChecks/RandomnessSecure.php', 'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => $baseDir . '/../lib/SetupChecks/ReadOnlyConfig.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index ec3304c1e7530..b8376dfc8a7a8 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -146,6 +146,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\SetupChecks\\PhpOpcacheSetup' => __DIR__ . '/..' . '/../lib/SetupChecks/PhpOpcacheSetup.php', 'OCA\\Settings\\SetupChecks\\PhpOutdated' => __DIR__ . '/..' . '/../lib/SetupChecks/PhpOutdated.php', 'OCA\\Settings\\SetupChecks\\PhpOutputBuffering' => __DIR__ . '/..' . '/../lib/SetupChecks/PhpOutputBuffering.php', + 'OCA\\Settings\\SetupChecks\\PhpSAPI' => __DIR__ . '/..' . '/../lib/SetupChecks/PhpSAPI.php', 'OCA\\Settings\\SetupChecks\\PushService' => __DIR__ . '/..' . '/../lib/SetupChecks/PushService.php', 'OCA\\Settings\\SetupChecks\\RandomnessSecure' => __DIR__ . '/..' . '/../lib/SetupChecks/RandomnessSecure.php', 'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => __DIR__ . '/..' . '/../lib/SetupChecks/ReadOnlyConfig.php', diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index 5faf7b5110b0c..d14cb0b7cac45 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -65,6 +65,7 @@ use OCA\Settings\SetupChecks\PhpOpcacheSetup; use OCA\Settings\SetupChecks\PhpOutdated; use OCA\Settings\SetupChecks\PhpOutputBuffering; +use OCA\Settings\SetupChecks\PhpSAPI; use OCA\Settings\SetupChecks\PushService; use OCA\Settings\SetupChecks\RandomnessSecure; use OCA\Settings\SetupChecks\ReadOnlyConfig; @@ -209,6 +210,7 @@ public function register(IRegistrationContext $context): void { $context->registerSetupCheck(PhpOpcacheSetup::class); $context->registerSetupCheck(PhpOutdated::class); $context->registerSetupCheck(PhpOutputBuffering::class); + $context->registerSetupCheck(PhpSAPI::class); $context->registerSetupCheck(RandomnessSecure::class); $context->registerSetupCheck(ReadOnlyConfig::class); $context->registerSetupCheck(RepairSanitizeSystemTagsAvailable::class); diff --git a/apps/settings/lib/SetupChecks/PhpSAPI.php b/apps/settings/lib/SetupChecks/PhpSAPI.php new file mode 100644 index 0000000000000..9741bc55dba0f --- /dev/null +++ b/apps/settings/lib/SetupChecks/PhpSAPI.php @@ -0,0 +1,89 @@ +l10n->t('PHP-FPM process limit'); + } + + #[\Override] + public function getCategory(): string { + return 'php'; + } + + #[\Override] + public function run(): SetupResult { + if (php_sapi_name() !== 'fpm-fcgi') { + return SetupResult::success(); + } + + if (!function_exists('fpm_get_status')) { + return SetupResult::warning( + $this->l10n->t('PHP-FPM status is unavailable, so the max children check could not be completed.'), + $this->urlGenerator->linkToDocs('admin-php-fpm') + ); + } + + $status = fpm_get_status(); + if ($status === false) { + return SetupResult::warning( + $this->l10n->t('PHP-FPM status could not be read, so the max children check could not be completed.'), + $this->urlGenerator->linkToDocs('admin-php-fpm') + ); + } + + $maxChildrenReached = $status['max-children-reached'] ?? null; + $maxActiveProcesses = $status['max-active-processes'] ?? null; + $startTimeEpoch = $status['start-time'] ?? null; + + if (!is_int($maxChildrenReached) || !is_int($maxActiveProcesses) || !is_int($startTimeEpoch)) { + return SetupResult::warning( + $this->l10n->t('PHP-FPM status is incomplete, so the max children check could not be completed.'), + $this->urlGenerator->linkToDocs('admin-php-fpm') + ); + } + + if ($maxChildrenReached > 0) { + $startTime = date('Y-m-d H:i:s T', $startTimeEpoch); + $timesReached = $this->l10n->n('once', '%n times', $maxChildrenReached); + + $message = $this->l10n->t( + 'PHP-FPM reached its configured "pm.max_children" limit %s since its last restart at %s (peak active processes: %s).', + [$timesReached, $startTime, (string)$maxActiveProcesses], + ); + $message .= ' ' . $this->l10n->t( + 'When the pool is saturated, additional requests may be queued, time out, or fail. This can cause slow responses, client connection errors, or 502/504 gateway errors.' + ); + $message .= "\n\n" . $this->l10n->t( + 'The counter resets when PHP-FPM restarts. If saturation is ongoing or recurring, review pool configuration and server capacity and consider increasing "pm.max_children".' + ); + + return SetupResult::warning( + $message, + $this->urlGenerator->linkToDocs('admin-php-fpm'), + ); + } + + return SetupResult::success(); + } +}