php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #74178 Suggestion/Idea: Stateful / Application Script
Submitted: 2017-02-27 18:34 UTC Modified: 2017-02-28 05:33 UTC
From: alex at alex-at dot ru Assigned:
Status: Open Package: FPM related
PHP Version: Next Major Version OS:
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2017-02-27 18:34 UTC] alex at alex-at dot ru
Description:
------------
Many current PHP scripts suffer from necessity of (re)initializing lots of state from startup to real serving. Even using Opcache/Memcached/whatever, initialization of all database/cache/etc. connections and classes takes ages.

So why just not bring common 'appserver' paradigm to PHP?

Basically, it all boils to implementation of a single function: run_as_stateful($callback, $timeout = null) or something like that.

What should it do: it should clean up all volatile request and session variables, and make server know script is ready to accept new request to it. Everything else related to process should remain intact: variables, classes, objects, database/cache connections, open files, other resources, whatever.

When a new request to the same script comes, and there is some stateful script process idling, request gets scheduled to one of these processes. Request variables get filled (session ones do not, until new session_start call in PHP code), and $callback gets executed. Basically, that's all. This will allow $callback to skip all the unnecessary initialization, getting through only to request-based serving. 

Once $callback executes, it can either "exit", causing script process to terminate, or just "return", causing script to wait for a new request, or call run_as_stateful() anew, refreshing $timeout. Any error of course causes script process to terminate. 

$timeout here is a timeout for script process to be waiting for requests (idling) in total (each period counts, or may be refreshed within $callback), after which process terminates.

If there are no running stateful script processes at the moment of request, request is scheduled to normal PHP execution strategy, without any changes required.

File change detection can be optionally either ignored or delegated to opcache with its change detection strategy, and terminate all idling stateful script processes / set terminate flag to all running stateful script processes so they terminate at next idle loop.

The idea/suggestion is mainly intended for FPM I think, because it has process control and can be extended with any tunables needed, like maximum lifetime of a stateful script, maximum count of stateful scripts running (total and per script), list of scripts that are allowed to run as stateful, etc.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-02-27 19:00 UTC] alex at alex-at dot ru
Elaborating further (of course don't know if the idea is viable as a whole, but nevertheless).

max_execution_time may (should) be counted anew for each request. Something like max_stateful_execution_time should control total execution time, but only inside run_as_stateful() code, not at any moment, to make sure last request is complete.

run_as_stateful() never exits but may also be extended with optional $eventHandler and $eventInterval. If no requests come under $eventInterval microseconds, $eventHandler callback may be called to process i.e. external ZeroMQ or other events periodically, updating internal state.

From scripts developer perspective, applications handling a lot of state and serving lots of clients would benefit a lot from lack of 1) code reloading, 2) PHP engine initialization, 3) internal class reinitialization. Some messaging queues like ZeroMQ can also be used to synchronize state between running stateful scripts, reducing need to reload any state even further. Also similar mechanisms can potentially be used for i.e. websocket handling in parallel with handling requests.
 [2017-02-27 19:06 UTC] spam2 at rhsoft dot net
> So why just not bring common 'appserver' paradigm to PHP?

because when you need a app-server than just use a app-server and don't abuse PHP

if "initialization of all database/cache/etc. connections and classes takes age" the just seek the bloat in your application - that said from a happy user which has generate times between 1.1 and 3.5 ms including connect to database, load templates, load cached contents just because our code is designed to have a small footprint and oads things on demand but nothing unneeded

NO don't introduce all sort of bugs and race-conditions by trying to make something out of PHP which is not PHP - a with little exceptions "shared-nothing" architecture
 [2017-02-27 19:22 UTC] alex at alex-at dot ru
>>> because when you need a app-server than just use a app-server and don't abuse PHP

The whole point of this feature request is that PHP can easily be evolved to utilize best of both worlds. 

Reloading less state is obviously good, but manageability and extensibility of PHP code is obviously good as well. So when choosing between PHP/niche appserver, it's not an easy choice. 

The appserver choice also requires a whole second stack, meaning more developer time and skills are needed.

>>> that said from a happy user which has generate times between 1.1 and 3.5 ms

Man. 1.1 to 3.5 ms response time is good, but not any news. It's only 300 to 900 requests per second. Also, what's good in our case can be not attainable for larger applications with larger state. 

I.e. we have a real world running PHP application that handles ~1000 requests per second per 8 cores as well (2 to 3 requests per second from 300-500 simultaneous 'realtime' clients), connecting to MySQL and ZeroMQ and keeping a lot of state in multilevel cache. 

It really could benefit a lot from appserver strategy... if there was one. Reducing cache complexity and cutting off all complex state caching code on the way.
 [2017-02-27 19:30 UTC] spam2 at rhsoft dot net
> Man. 1.1 to 3.5 ms response time is good, but not 
> any news. It's only 300 to 900 requests per second

check your math!

on a i7 from 2011 it's 1200 request per seconds, on our current hardware it's 4500 requests per second with small contents and with larger contents it saturates easily the gigabit network and PHP is for sure not the bottenleck
 [2017-02-27 19:45 UTC] alex at alex-at dot ru
1000/1.1 is obviously about 909 requests per second per core. With more cores it should be more of course, but not linearly. And again: it heavily depends on application itself.

In our case, final content is quite small, and will never saturate even 100Mbps. It's using ~50% CPU time in FPM process, and most of that time is taken by script initialization. We need to reconnect to memcached, to MySQL, open some files for second level cache and pre-connect to ZeroMQ for case of cache miss (connection and socket start is asynchronous, but takes time). In a case of both levels cache miss we sometimes need to wait a bit of time for ZeroMQ data to arrive from publisher. This ZeroMQ magic could be autonomously happening in background if script was running in 'appserver' mode, making latency of cache misses negligible.
 [2017-02-27 19:52 UTC] spam2 at rhsoft dot net
> 1000/1.1 is obviously about 909 requests per second per core

seriously - it don't work that way in no environment as well as "oh, i double the cores and get twice requests per second" don#t work in real life

also no idea why the feature request is "FPM related" and if what ever you consider would be FPM specific and not work the same way with mod_php you clearly see it has no place in the ecosystem 

get xdebug and look where you really lose performance

at the same time realize that benchmarking is *not* that easy as you may think - when as example mod_defalte is part of the game php requests are even faster handeled than static files with the same content

so before you think you need new features please learn how to benachmark na drealize *waht* youa re benchmarking in reality - often this are two different things
 [2017-02-27 20:02 UTC] alex at alex-at dot ru
>> seriously - it don't work that way in no environment
I don't know where you got this 'double cores = double requests' thing. I'm talking about quite the contrary as well. For math, 1000/1.1 = 909.something, do you want it or not. Your i7 was able to run 1.3 request in parallel at average, hence the ~1200.

---

Next thing, I'd prefer to stop the empty bickering here, it's completely pointless.

The whole point of this ticket is that _realtime_ applications like gaming, monitoring, telemetry or realtime process management can all be encompassed by PHP as well with a relatively small change not introducing any heavy complexity internally.

I'm quite sure PHP devs already considered this one multiple times, so this is just in case it simply got lost/forgotten in the pile of things to evolve :)
 [2017-02-27 20:10 UTC] alex at alex-at dot ru
By realtime, I mean applications dealing with lots of requests for partially changing state, where reloading all the state information all the time can be costly, uses lots of CPU time and eventually makes response times go out of required scale.

A very good example of it is large telephony system state. We need to push each extension state to each state database client approximately each 0.5 seconds. States and channels information gets pushed to application handling client request via ZeroMQ in real time (and it is combined with specific client data), but alas we have to reload full state each time each client request is started.
 [2017-02-27 20:12 UTC] spam2 at rhsoft dot net
problem is that you don't gasp how FPM works

isloated processes to overcome the problems of a threading server (memleaks, non-thread-safe libraries and many more) - whatever you cache and try to be stateful needs to consider how it can be shared between the processes which are there for one reason: isloation

frankly that don't work even for things existing over a decade like https://bugs.php.net/bug.php?id=73888 where even the real_path cache is completly useless in real workloads
__________________________

for that below you don't oly use the wrong programming language you are in fact most likely even use the wrong operating system, at least a default linux kernel is *not* RT capable at all

A very good example of it is large telephony system state. We need to push each extension state to each state database client approximately each 0.5 seconds. States and channels information gets pushed to application handling client request via ZeroMQ in real time (and it is combined with specific client data), but alas we have to reload full state each time each client request is started
 [2017-02-28 05:33 UTC] alex at alex-at dot ru
>>> for that below you ... use the wrong programming language
And it finally came to to what the entire request is about, why and what for. Thanks. People still think that way, and this feature can help reduce 'PHP is wrong for that' way of thinking at least a certain bit. 

First of all, 'PHP is wrong for that' thing is total BS, at least in our case. It already works for us, it took very small time to develop the app, the app is stable, manageable and satisfies clients' needs. Porting state generation / Asterisk/gateway/PBX/phones integration code to anything without handy things like associative arrays and lax typing would be so much pain in the neck and take so much time/effort that we don't even consider it viable.

Then, the performance is already ok for us. We see it can be improved in terms of performance by using different coding paradigm for parts of it, and see a clear way for PHP, hence the request.
 [2017-02-28 09:19 UTC] spam2 at rhsoft dot net
here you go: http://appserver.io/

if the initialization time is your problem than make your script a long running process the one or other way, initialization of all database connections is no problem - persistent connections exists
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sat Dec 07 06:01:24 2019 UTC