Schlafende PDO-Handles, die MySQL füllten
ATTR_PERSISTENT bei php-fpm, Horizon und schedule. max_connections 151. Too many connections mittags.
MySQL lehnte Verbindungen gegen Mittag ab. Die Liste war in Ordnung. SHOW PROCESSLIST war fast nur Sleep. Einhundertvierzig Handles von php-fpm mit einer Query von vor zwanzig Minuten. Idle Horizon-Worker hielten acht weitere. wait_timeout war 28800, der Default. Persistent PDO tut, was es sagt. Der Worker hält TCP sein ganzes Leben.
pm.max_children war 8. Das sind acht Verbindungen, wenn jeder Worker eine hat. Recycles mit pm.max_requests öffnen eine weitere, wenn das alte persistente Handle noch im Prozess-Pool liegt. Horizon --max-processes und der schedule-Container nutzten dieselbe DSN mit ATTR_PERSISTENT. Compose skalierte php einmal für einen Spike und vergaß, zurückzugehen. 8 wurde 32. MySQL-Default ist 151. Die Sleep-Zeilen stammten vom letzten Scale-out, bis max_requests die Worker tötete.
Das Problem war persistentes PDO über php-fpm, Horizon und schedule, das max_connections 151 mit Sleep füllte. Editoren und Käufer bekamen too many connections mittags. Ich brauchte kurze Handles, wait_timeout 60 und dieselben 151 als harte Kappe, die ich nicht verstecke.
Kurzes Leben
ATTR_PERSISTENT ist aus. Jeder Request öffnet, fragt, schließt. Acht Worker mittags sind acht Verbindungen, nicht einhundertvierzig. Horizon nutzt eine Verbindung pro Prozess und schließt, wenn der Worker an --memory stirbt. wait_timeout ist 60. Ein übriges Sleep stirbt vor dem Nachmittags-Ausfall.
- Memcached und Redis nehmen die Liste von MySQL. Die restlichen Queries sind billig. Eine neue Verbindung pro Request ist billiger als ein volles Processlist.
- max_connections blieb 151. Hochsetzen versteckt die Sleep-Zeilen bis zum nächsten Scale-out.
- Der Healthcheck des mysql-Containers nutzt einen eigenen User. Er ist nicht einer der 140.
Threads_connected sitzt mittags nahe der Zahl beschäftigter Worker. Too many connections ging mit dem Persistent-Flag. Der Katalog brauchte nicht mehr MySQL. Er brauchte weniger Handles, die nichts taten.
Was ich daraus mitnehme
SHOW PROCESSLIST voller Sleep ist kein Query-Problem. Es ist ATTR_PERSISTENT plus langes wait_timeout.
Scale-out ohne Scale-in lässt Handles bis max_requests. Persistent multipliziert den Rest.
max_connections hochzusetzen versteckt den Graph. Ich behalte 151 und schließe TCP.
