
De Daemon: warme workers, geen opstartkosten
27 Mar 2026
Elke PHP-aanroep betaalt traditioneel een kleine belasting voordat het enige echte werk doet: het opstarten van het framework, het aansluiten van de app en vervolgens het afhandelen van de aanvraag. De worker-modus van FrankenPHP verwijdert al het meeste daarvan door de app resident in het geheugen te houden tussen aanvragen. De Phlo Daemon neemt hetzelfde idee en breidt het uit naar alles wat geen normale HTTP-aanroep is.
Wat heeft eigenlijk een daemon nodig
Drie dingen profiteren van een proces dat al warm is en al draait: Phlo Realtime (behandeld in een eerdere post), de runtime helpers (phlo_sync, phlo_async, await, phlo_stream) wanneer één aanvraag zich verspreidt in veel aanroepen, en geplande taken. De daemon is een pool van workers die elk Phlo-doel, een route, een methode of een taak kan uitvoeren, zonder per aanroep een nieuwe opstartkost te betalen.
prop tasks => arr(
cleanup: arr(do: 'session::prune', every: '15 minutes'),
report: arr(do: 'reports::sendWeekly', weekly: 'monday 08:00'),
)
Er is geen cron-syntaxis in de app zelf, alleen every:, daily: of weekly: tegen een eenvoudige schema-lezer. Dat schema bevindt zich in %app->tasks, met of zonder een daemon; wat verandert is de trigger. Zonder een daemon roept één generieke cron-regel tasks::run elke minuut aan en de resource zelf beslist wat er daadwerkelijk aan de beurt is. Met een daemon krijgt elke app in zijn hostmap diezelfde minuut-tik ingebouwd, zodat de ene cron-regel ook verdwijnt.
De runtime helpers werken op dezelfde manier: zonder de daemon constante ingesteld, draaien phlo_async/await nog steeds als eenmalige achtergrondprocessen, elk met een nieuwe opstart. Stel daemon: 3001 in phlo_app(...) in en dezelfde aanroepen worden in plaats daarvan naar de residentiële worker pool geleid, zonder opstart per aanroep. De winst is het grootst precies waar één verzoek zich verspreidt in veel aanroepen; await() over honderd ontbrekende vertalingen is honderd opstarts op het eenmalige pad, honderd dispatches op een warme pool met de daemon.
Optioneel bij constructie
Core Phlo vereist de daemon niet, en de twee dingen die het toevoegt falen anders zonder een. Realtime heeft echt geen fallback: geen daemon betekent geen WebSocket-server, punt. Taken hebben er wel één: zonder een daemon doet een enkele cron-regel die tasks::run elke minuut aanroept precies wat de ingebouwde tik van de daemon doet, alleen met één crontab-entry om te beheren in plaats van nul. Dat is operationeel belangrijk: je kunt een Phlo-app ontwikkelen en implementeren zonder enige daemon, leunen op cron voor planning, en de daemon later toevoegen op het moment dat Realtime of een fan-out-zware aanvraag er daadwerkelijk om vraagt, zonder iets te herstructureren dat al bestaat.
De vorm ervan in productie
In een productie-indeling is de daemon een sidecar naast FrankenPHP, beide verbonden door hetzelfde serverplatform: de /server-setup builder geeft FrankenPHP een echte systemd-eenheid en draait de daemon onder pm2, zelf geregistreerd bij systemd zodat deze weer tot leven komt bij opstart. Het is één extra warme proces op de box, geen aparte fleet om te bedienen. De afstemming die belangrijk is, is het aantal workers ten opzichte van beschikbare geheugen, dezelfde afstemming die elke resident-process runtime nodig heeft, en het schaalt op dezelfde manier als de rest van Phlo: horizontaal, met identieke nodes achter een load balancer, elk met zijn eigen daemon voor zijn eigen app.
Het punt is niet "cron is slecht." Het is dat geplande taken en realtime taken dicht genoeg bij elkaar liggen in vorm, een achtergrondproces dat je code draait zonder opstartkosten, dat ze één antwoord verdienen in plaats van twee niet-verwante antwoorden.