CV laden
Zurück zu Business Cases

Verbindungen

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.

Drei Compose-Dienste, ein max_connections. Persistent heißt, der Slot ist während Sleep belegt.
Drei Compose-Dienste, ein max_connections. Persistent heißt, der Slot ist während Sleep belegt.

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.

Idle-TCP ist nicht gratis. Es ist ein Slot. wait_timeout hilft nur, wenn PDO nicht persistent ist.
Idle-TCP ist nicht gratis. Es ist ein Slot. wait_timeout hilft nur, wenn PDO nicht persistent ist.
  • 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.

Zurück zu Business Cases