3

I am trying to implement simple longpolling in php, my server code goes like this:

if (!isset($_SESSION["lastResult"])) {
    $_SESSION["lastResult"] = "";
}

while(true){
   $sql = "SELECT * from table";
   $result = mysqli_query($conn, $sql);

   $array = [];
   while ($row = $result->fetch_array()) {
        do something and insert each element into $array;
   }
   $jsonRes = json_encode($array);

   //if old array is different from the new one, that means information has been updated, 
   //so I need to echo new data to user.

   if (strcmp($jsonRes, $_SESSION["lastResult"]) == 0) {
        echo $jsonRes;
        $_SESSION["lastResult"] = $jsonRes;
        exit();
    }

    //If information hasn't changed, just sleep for 5 seconds and repeat next time.
    sleep(5);
}

and client code:

  const waitForData = () => {
    //this is just a js fetch function
    getDataHTTP().then(res => {
      this.setState({ itemsData: res });
      waitForData ();
    });
  };
  waitForData();

But what happens is in my network tab, I see my getData php script "pending" as it should, but its pending forever, and the other thing is that I can't really edit any other data on the webpage, because for some reason, which I assume is the sleep() function, All my php scripts stop working, and are forever "Pending".

Thanks in advance.

Edit:

Just for the record, $_SESSION variable works, all data is where it should be. Same is with SQL query, it returns everything correctly.

Edit2:

I am not getting any errors except max execution time.

Nick
  • 455
  • 9
  • 28
  • `result` < is seen as a constant. `mysqli_query($sql)` is missing the connection. – Funk Forty Niner Jan 07 '20 at 16:05
  • @FunkFortyNiner Sorry, typo, but this is not a real code at all. I need to edit this in. – Nick Jan 07 '20 at 16:06
  • Well, you would be surprised as to what people will submit for answers, when they see code like that. Please post actual code. Edit; thanks. – Funk Forty Niner Jan 07 '20 at 16:07
  • @FunkFortyNiner I am sure that my SQL query has nothing to do with the long-polling, while loop and sleep() function, I could have written anything else there instead of sql query. But thanks for pointing out, I'll add more details. – Nick Jan 07 '20 at 16:08
  • Welcome. Anything from/by enabling error reporting? – Funk Forty Niner Jan 07 '20 at 16:09
  • @FunkFortyNiner No errors at all. Only max execution time exceeded. – Nick Jan 07 '20 at 16:10
  • @Nick Can you read error_log file ? –  Jan 07 '20 at 16:11
  • @Dilek I did. Only as expected, max_exec_time errors. – Nick Jan 07 '20 at 16:13
  • @Nick Try adding `set_time_limit(0);` taken from [this Q&A](https://stackoverflow.com/q/5164930/1415724) here on Stack O. See the other answers in there also. This could also be your db taking up the time, but I can't say for certain. – Funk Forty Niner Jan 07 '20 at 16:17
  • Does the data in the array ever get updated? Or has it become a catch22. It waits for data to be changed, that can't be changed because the script is pending. – Mouser Jan 07 '20 at 16:19
  • @Nick See this question for setting time limit https://stackoverflow.com/questions/51719047/check-if-max-execution-time-and-memory-limit-is-set-to-desired-values –  Jan 07 '20 at 16:21
  • @Mouser I mean, It'd get updated if my WHOLE server didn't go *sleep* because of this one script :D – Nick Jan 07 '20 at 16:21
  • @FunkFortyNiner set_time_limit(0) didn't work – Nick Jan 07 '20 at 16:23
  • if(){ sleep() function will efect to queries in if statement } But if you set sleep() some where in your page then it will efect all page, that is not the all codes in page, probably while loop is repeating sleep function, Try it out of the while loop –  Jan 07 '20 at 16:25
  • Look into this: https://www.php.net/manual/en/ev.examples.php – Mouser Jan 07 '20 at 16:27
  • @Mouser Thanks for an idea, but unfortunately PECL extension library is not available on my live hosting – Nick Jan 07 '20 at 16:32
  • @Dilek But this is what its supposed to do, it should pause "infinite" loop for few seconds before next check, and do it until change will be made. Then loop closes. – Nick Jan 07 '20 at 16:36
  • @Nick it wont because its repeating I just tested it, dont know how you can use in loop but, the peroblem is while loop repeating your sleep function time and driving query to reach memory limit –  Jan 07 '20 at 16:40
  • @Dilek Yes, but for example look: https://stackoverflow.com/questions/15414605/php-sleep-delay They say sleep(n) pauses a loop too. – Nick Jan 07 '20 at 16:45
  • @Nick just copy and paste my code in answer and see the result dont change anything –  Jan 07 '20 at 16:48
  • 1
    `while true { blah blah, then (without checks) sleep(5); }` This will NEVER end! – delboy1978uk Jan 07 '20 at 16:49
  • Rain, it won't. – delboy1978uk Jan 07 '20 at 16:51
  • @Dilek ummm, yes? And I know 100% that this fetch-array works and its not getting looped in forever, because if I die(var_dump($arr)); right after it I get the result. – Nick Jan 07 '20 at 16:53
  • My design for such a function would be AJAX-driven from the client, not a poll-loop on the host. If you "put PHP to sleep," as you see, *the interpreter stops cold.* – Mike Robinson Jan 07 '20 at 17:05
  • @MikeRobinson Right, but people have achieved this working(For example at facebook, they use longpolling too), and I doubt I'll stop until it works – Nick Jan 07 '20 at 17:10
  • @Nick I dont think you will understand what people says 1. While is not an if statement wont check for true, 2. you cant use while as an if statement. 3. comments in your while loop, each one is a function of php, I told that many times you still didnt remove. So good luck, Last point your loop is runing infinite, because of `do();` and `each();` and `$array` –  Jan 07 '20 at 17:28
  • @Dilek https://stackoverflow.com/questions/1086380/how-does-facebook-gmail-send-the-real-time-notification Look at an accepted answer, he is using while too – Nick Jan 07 '20 at 17:28
  • @Dilek I could as well do `while($done == false) { if(smtn) $done = true; }` It doesn't change anything – Nick Jan 07 '20 at 17:36
  • That answer about Facebook is over a decade old; I remember when Comet and long polling was all the rage on SO, since it was really all that was viable. It seems a little foolhardy to go to the wall for an old and somewhat problematic methodology developed in a less-than-capable era, than to prefer better aligned solutions like web sockets, socket.io, pub/sub, et al. To each their own. I really doubt Facebook engineers are still using long polling anymore. – Jared Farrish Jan 07 '20 at 18:07
  • @JaredFarrish Thanks. I've already had experience with websockets. But In my case, I can't use them because godaddy linux server doesn't provide possibility to run a php script in separate thread – Nick Jan 07 '20 at 18:14
  • Long polling locks the process that's running it (this is what Facebook battled, which led to Hack VM and php-fpm and the takeover of nginx). If I were working a problem like this, I would create an alternate simpler scenario, get that working to learn the underlying process, then slowly add back the domain-specific parts. First get a long polling scenario working at all (which will also make questions here simpler), then adjust with what you've learned. – Jared Farrish Jan 07 '20 at 18:18
  • @JaredFarrish Thank you, now I have learnt that sleep pauses whole process and not just a single script. – Nick Jan 07 '20 at 18:25
  • Long polling is getting around the (web 1.0's) statelessness "problem". Hence, it's a hack, and one of the ugly sides of that is resource squatting. Processes are resources writ large. Long running background processes often built around the same idea of `while(true)` now consume and distribute to listeners. It's possible PHP is much better at managing socket connections, but Node is pretty good in this regard, or services like Pusher. – Jared Farrish Jan 07 '20 at 18:30
  • @JaredFarrish Thanks for even more info. I wish I was born earlier so I knew that much for now. Though I still want to implement longpooling for educational purposes – Nick Jan 07 '20 at 18:34
  • 1
    @Nick The problem is not the `sleep()` statement. The real problem is that you are keeping the session open and locked in the thread using the `sleep()` statements. Other requests waiting in their `session_start();` call to work with the session, which is currently used by the other PHP script. See questions like https://stackoverflow.com/questions/3371474/php-sessions-is-there-any-way-to-disable-php-session-locking – Progman Jan 07 '20 at 19:38
  • @Progman Thank you all, and I got it working. Now instead of $_SESSION I just save table's last change timestamp in DB, and then send it to client, and client saves it in cookie using JS. Then in server code I access $_COOKIE["lastChange"] and everything else is similar. – Nick Jan 08 '20 at 09:20
  • @Nick You can add an answer and accept it pointing out your trouble was the session initiation and explain how you got around it. It might help someone else in the future. – Jared Farrish Jan 09 '20 at 19:41
  • @JaredFarrish I'd love to. But i can't add an answer due to question being flagged as "off topic" – Nick Jan 10 '20 at 10:01
  • 1
    @Nick Voting to reopen. It's clearly a programming problem with a clear solution, if someone were willing to omit session. – Jared Farrish Jan 10 '20 at 14:01

1 Answers1

0

Look at your code:

while(true){  // do this FOREVER unless we break the loop
   $sql = SELECT * from table;    // I assume we always get all rows?
   $result = mysqli_query($conn, $sql);
   $array = [];

   while ($row = $result->fetch_array()) {
        // Do stuff
        $array[] = $row;
   }
   // now array has all rows

   $jsonRes = json_encode($array);

   if (strcmp($jsonRes, $_SESSION["lastResult"]) == 0) {
        echo $jsonRes;
        $_SESSION["lastResult"] = $jsonRes;
        exit();
    }
    // so just sleep 5 seconds and do it all over again
    sleep(5);
}

Wouldn't it be easier to have a datetime column in the table, and if a select comes back with rows of an update time later than the last time the thing ran, then select * and return? whith no while true loop?

delboy1978uk
  • 12,118
  • 2
  • 21
  • 39
  • 1
    Session last result is set if it doesn't exist, and its set right after the echo $jsonRes. – Nick Jan 07 '20 at 16:57
  • yeah just seen that lol. why not upon a row update set an updated time instead of this weird solution? – delboy1978uk Jan 07 '20 at 16:58
  • Because I really wanted to avoid having one more table in db for that component, though now that I think of it, seems like it'd be more performant, still I don't think it'd fix the current problem – Nick Jan 07 '20 at 17:00
  • it'd be just one more column with a timestamp – delboy1978uk Jan 07 '20 at 17:02
  • 1
    Thanks for an awesome and better alternative to my SELECT * from table, but I really want to make it realtime, so whenever someone somewhere changes some value in the table, this component will update in the live too. So I'd still need a longpolling solution. – Nick Jan 07 '20 at 17:06
  • 1
    you could use JS to make an AJAX call every X seconds, and just send the fresh results each time – delboy1978uk Jan 08 '20 at 11:29
  • Check my last comment on my root question (32nd :p) I've actually managed to do a longpolling but instead of php $_Session I just used $_Cookie, because session start was locked in a while loop. But cookie does not get locked. I hope it will be useful for people in future. – Nick Jan 08 '20 at 14:53