Waking up PHP scripts?

iljitsch

Ars Tribunus Angusticlavius
8,474
Subscriptor++
Once in a while, I do training courses. I made a web page that allows me to send info that is subject to change to the participants. Right now this is all done with polling in Javascript, but the problem there is that either the polling rate needs to be quite high, or it takes a while for all the users to see the most recent info.

So what I want to do is to poll relatively infrequently, but for the AJAX calls that are handled on the back end by a PHP script to hang around until updated information comes along. I just can’t figure out a good way to do this. The critical part here is waking up a PHP script that is currently de facto asleep.

I do have something working with SystemV message queues. But there are two issues with that. I can send messages to an arbitrary receiver, but just not to all receivers. Right now I just send a message to any receiver, and that receiver repeats the message, so eventually they should all get it. But that seems to brittle to me. An alternative is keeping track of all the receivers... Also, with blocking message reads, there are no timeouts, so it’s possible for large numbers of these scripts to get stuck and eat up memory on the server.

I also looked at signals. This seems to work well enough when I run PHP scripts through the command line, but somehow the signals never reach the scripts when they run through the web server. And I’d have to keep track of the PIDs of the different scripts, with the possibility that signals that end up on a thread that isn’t running this particular script might get interrupted.

Ideally, I’d just point PHP to a file and tell it to wake up when that file changes, or after x seconds if it never changes.

I know web sockets would solve this, but then I’d have to come up with server software to handle those, which seems like a larger project than I’d like.

Suggestions?
 
Last edited:

ImpossiblyStupid

Smack-Fu Master, in training
81
Subscriptor
Well, you're kind of all over the place and I'm not getting any real sense of what you're aiming for or example code. On the client side of things, if you don't want to mess with WebSockets just yet (or at all), maybe start with server-sent events. On the server side of things, my PHP is a bit rusty, but if there wasn't something good built in, I'd go looking for a library that supported some common, "simple" synchronizing protocol. Maybe inotify for something file-based, or a basic message queue like MQTT, or something like Redis if you want to start doing really fancy stuff.
 

iljitsch

Ars Tribunus Angusticlavius
8,474
Subscriptor++
I’m all over the place because I’m looking for a place where I can get what I need...

Yes, server-sent events are better than AJAX polling. I actually have something working with SSE with PHP, where the PHP scripts that the clients connect to do a blocking read of a message queue and wake up when there is a new message. I tried to include some code but the forum wouldn’t let me. Probably too many special characters.

But see my issues with that above. There are probably workarounds, but it seems like inotify could be a good alternative, if the installation process within PHP isn’t too difficult.

Yes, having a PHP script persist for each client is not very scalable. It gets worse if those scripts may hang indefinitely because of a message queue read that won’t timeout.

A better solution would be a daemon that accepts SSE or websocket sessions where very many of those are handled by a single process. So little process overhead, and the per-session overhead will also be pretty low, basically just a network socket. If you can then send the same update to all connected clients when a certain event happens, life is pretty scalable.
 

ImpossiblyStupid

Smack-Fu Master, in training
81
Subscriptor
Yes, having a PHP script persist for each client is not very scalable. It gets worse if those scripts may hang indefinitely because of a message queue read that won’t timeout.

Those are server/protocol level issues. I've done basic SSE with straight CGI on Apache and it's not been a problem resource-wise on a modern server. Connections are dropped after 5m by default, so I just have a 299s timeout to avoid warnings/errors due to that; clients reconnect automatically regardless. If you have tighter requirements, maybe look at tricking out something more advanced with lighttpd.

A better solution would be a daemon that accepts SSE or websocket sessions where very many of those are handled by a single process. So little process overhead, and the per-session overhead will also be pretty low, basically just a network socket. If you can then send the same update to all connected clients when a certain event happens, life is pretty scalable.

And that's why protocols like MQTT exist! ;) Nothing you're doing screams HTTP to me, other than your desire to make the UI a web page. Pick a pubic MQTT server, use a JavaScript client library, and done.
 

ImpossiblyStupid

Smack-Fu Master, in training
81
Subscriptor
Well, "generic" in the sense that every MQTT server that I'm aware of supports a WebSocket interface, and that's what the various JavaScript libraries use. You probably don't even need to run your own server if your needs are as modest as they sound. The devil is always in the details, of course, but my inclination is still to decouple the pubsub mechanism from the web stack.
 

w00key

Ars Praefectus
5,908
Subscriptor
What database do you use? This seems like a use for Postgresql's NOTIFY and LISTEN, which just lets you subscribe to a channel and broadcast a string on it. Listen on clients, wait for message, and whoever makes the change posts the change counter or whatever on the channel after it is done and everyone can send the result to the client.

On anything else you can probably fake it with SELECT FOR UPDATE on a signal row on the writer and everyone else just blocks on the lock, and COMMIT to release + let everyone select the latest change. But this only works with 1 writer and many listeners.
 

iljitsch

Ars Tribunus Angusticlavius
8,474
Subscriptor++
Ok I’ve figured it out! Thanks to ImpossiblyStupid (who now needs to find a new user name) for pointing me in the direction of inotify.

I’m in the process of moving from MySQL/MariaDB to SQLite. The C interface exposes some LISTE / NOTIFY functionality but as far as I can tell PHP can't access that.

It was a slight hassle to install the inotify extension for PHP, but now I can "watch" files and use stream_select() on that, which will wait for something to happen but you get to specify a timeout. And of course multiple instances of the script can watch the same file at the same time and they are all notified.

So now my script will sleep until there is a change to the database, but if that doesn’t happen within a reasonable amount of time, the script can exit cleanly to avoid bogging down the server. (This training course stuff runs on a Raspberry Pi 400 with a dozen Docker containers going about their business...)
 
Last edited:

ShuggyCoUk

Ars Tribunus Angusticlavius
9,975
Subscriptor++
mild necro. Everything these days is an event loop (which subtle and significant varieties within that, but the 'don't call me, I'll call you' part is the same.
What you need to do is work out how to plug into the event loop (and normally someone has already done that for your platform of choice and you should just use that). The rest is client specific - and nowadays there's javascript and server side libraries to wrap all the actual internal stuff that (including making it work on older browsers as needed) for you - use those. On ASP.Net that used to be SignalR but dobtless that's old hat these days (I am not a very good web dev).