; ============================================================================ ; Fast pool - the default for every PHP request not matched by the long-pool ; LocationMatch. ; ; Serves THREE applications, because conf-available/php-fpm-handler.conf is ; Included by three vhosts: ; solutions.businessmap.io -> /var/www/production/public (businessmap-sa) ; portal.businessmap.io -> /var/www/portal/backend/public (Alias /api/ etc.) ; devportal.businessmap.io -> /var/www/devportal/backend/public ; Every limit below is therefore shared: a spike or a wedged endpoint in one app ; degrades the other two. php8.4-fpm does not exist on this host, so portal and ; devportal running on 8.3 is deliberate. Split the pools if that coupling bites. ; ; Requests that legitimately run for minutes to hours belong in the [long] pool - ; see conf-available/businessmap-sa-long.conf (solutions vhost only). ; ============================================================================ [www] ; Pool user/group MUST own writable/{logs,cache,session,uploads} for all three apps. ; Confirmed on this host: stat -c '%U:%G' /var/www/production/writable/logs ; -> www-data:www-data user = www-data group = www-data ; Unix socket reached by Apache through the SetHandler in php-fpm-handler.conf. ; (ProxyPassMatch is how the LONG pool is reached, not this one.) Must be readable ; and writable by the Apache user. listen = /run/php/php8.3-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 ; Dynamic process management for bursty interactive traffic. ; ; NO LONGER far below the ceiling. This pool hit 40/40 in three separate hours on ; 2026-08-24 (00, 12 and 14 UTC), with max_children_reached climbing to 4 and the ; accept queue reaching 119 unaccepted connections (sockq, not FPM's listen_q, which ; reads 0 on a unix socket). healthcheck.php degraded to a 191s p95 and the ALB pulled ; the instance. ; ; Do NOT read that as needing more children. The cause was CodeRunner executions ; sleeping inside BusinessmapConnect's 429 retry -- workers held while consuming no ; CPU -- so raising max_children buys more sleepers. Those routes have their own pool ; now (codeRunner.conf); this comment stays as the reason not to re-solve it here. ; Counters reset on reload, so a current max_children_reached of 0 is not evidence. pm = dynamic pm.max_children = 40 pm.start_servers = 8 pm.min_spare_servers = 4 pm.max_spare_servers = 12 pm.max_requests = 500 ; Query with cgi-fcgi against the socket above. Unlike the long pool, the per-worker ; "request URI" IS meaningful here: SetHandler passes the real URI through, whereas ; the long pool's ProxyPassMatch rewrites it to /index.php before FPM sees it. pm.status_path = /fpm-status ; --- Timeouts ------------------------------------------------------------ ; Wall-clock hard kill, and the governor that actually fires. New under FPM - mod_php ; had no wall-clock limit at all. Apache's ProxyTimeout is 310s, deliberately just ; above this, so FPM hits its own limit first and PHP logs a real error instead of ; the client getting a bare 502. ; ; KNOWN GAP: routes still in this pool that ask for more than 300s are cut here - ; customUpdates / printCardFilter / boardSettings (600), internal/* (600, ; copyTemplate 1200), subscriptions / moveCards / planner2Bmap / inviteUsers (600), ; and HTTPConnect requests 330 on every connector call. Move them to the long pool ; or raise this if it starts to bite. request_terminate_timeout = 300s ; CPU-time ceiling. On Linux this counts CPU only - sleep() and network waits do not ; accrue, which is why no "Maximum execution time" fatal has ever been logged here ; despite the routes listed above. php_admin_value, so set_time_limit() CANNOT raise ; it; that is deliberate, because request_terminate_timeout is the same 300s and is ; wall-clock, so it fires first in every realistic case. php_admin_value[max_execution_time] = 300 ; Slow requests with a PHP backtrace. Worth knowing what this has actually caught: ; all 10 entries as of 2026-08-14 were portal's EmbedLaunch sitting in sleep() inside ; its own HTTP retry loop - not businessmap-sa at all. Check script_filename before ; assuming a slow request belongs to this app. request_slowlog_timeout = 30s slowlog = /var/log/php8.3-fpm/www-slow.log ; Must stay off: CI4 throws if it is enabled (app/Config/Events.php:28) and the DW ; gateway streams pre-gzipped bytes. php_admin_value[zlib.output_compression] = Off ; Per-request ceiling. 40 x 256M is 10GB of theoretical worst case against 7.6GB of ; RAM, but reaching it needs 40 simultaneous memory-heavy requests: a fresh worker is ; ~14MB resident (summed RSS overstates this, since forked workers share most pages ; copy-on-write). ; ; CORRECTION: "no [pool www] OOM has ever been logged" was true when written and is ; not any more -- the metrics archive records memory_limit fatals in this pool on ; 2026-08-24 at hours 00, 12 and 14 UTC (1, 1 and 2 respectively), the same three ; hours the pool sat at 40/40. Recheck with: ; sudo grep 'Allowed memory size' /var/log/php8.3-fpm.log | grep -c '\[pool www\]' php_admin_value[memory_limit] = 256M catch_workers_output = yes