4

I have a connector that will call a RESP API using cURL and PHP.

I need to call one method every second to check for new messages and then process them. I used the following 2 approaches to handle the messages

  1. AJAX Polling using SetInterval(): call the php script once every second. This works perfectly except I am unable to prevent multiple SetInterval() from running at the same time from different tabs of the browser. ( I don't want to have user opens 10 browser tabs which leads to one user having 10 SetInterval() are running at the same time.
  2. Server Sent Events using EventSource: The server will send update the browser once there are new data in the queue. This slows down the respond time. Every call I make to the script takes about 20+ seconds to complete which is a problem. I am not sure why this is happening.

Here is my SetInterval() implementation

function startCalls(){
    //update the screen using Intervals
    refreshIntervalId = setInterval(function() {

        $.getJSON("index.php", {'method': 'getMessages', 'jSON': true} , function(data){
            processServerData(data);
         });

    }, 1000);
}

once a user logs in I calls this function startCalls()

inside the index.php file I have this code to be called

if($method == 'getMessages'){

    $messaging = new ICWS\Messaging($icws);
    $messaging->processMessages();
    $myQueue = $messaging->getCallsQueue();
    echo json_encode($myQueue );

}

Here is my second implementation "Server-Sent Events"

//Server Side Message Polling
function startPolling(evtSource){

    evtSource.addEventListener("getMessagingQueue", function(e) {

        var data = JSON.parse(e.data);
        processServerData(data)
    }, false);
}

once a user logs in I calls this function startPolling( new EventSource("poll.php") );

For the sake of simplicity, assume that my processServerData method looks like this

function processServerData(data){
    console.log('process the data received from the server');
}

here is my php code

<?php

    require 'autoloader.php';

    //Make sure cURL is enabled on the server
    if(!is_callable('curl_init')){
        exit('cURL is disabled on this server. Before making API calls cURL extension must be enabled.');
    }

    if( isset($_COOKIE['icws_username']) ){
        $user = trim($_COOKIE['icws_username']);
    }

    if( isset($_COOKIE['icws_password']) ){
        $pass = trim($_COOKIE['icws_password']);
    }

    if( isset($_COOKIE['icws_station']) ){
        $stationName = trim($_COOKIE['icws_station']);
    }

    if( !isset($user, $pass, $stationName) ){
        echo json_encode(array('status' => 'The IC credentials you entered are invalid')); 
        exit;
    }

    $scheme = 'http';
    $host   = 'host';
    $port   = '8018';
    $sleepTime = 1;
    $url = sprintf('%s://%s:%s@%s:%s', $scheme, $user, $pass, $host, $port);

    try {
        header("Content-Type: text/event-stream\n\n");
        session_start();

        //configure the connection
        $conf = new ICWS\Config\Config($url, $stationName); 

        //create a new instance of icws
        $attrebutes = array();
        $icws = new ICWS\Connection($conf, $attrebutes, true); 

        $messaging = new ICWS\Messaging($icws);

    ob_end_clean();
    while(true){

        header("Content-Type: text/event-stream" . PHP_EOL);
        header("Cache-Control: no-cache" . PHP_EOL);

        $messaging->processMessages();
        $result = $messaging->getCallsQueue();

        //$current = $icws->getCurrentUserStatusQueue();
        echo 'event: getMessagingQueue' . PHP_EOL;
        echo 'data: ' . json_encode( $result) . PHP_EOL;    
        echo PHP_EOL; //required  

        ob_end_flush();
        flush(); 
        sleep(1);

    }

    } catch(Exception $e){
        echo $e->getMessage();
    }


?>

The server seems to lock all requests that hit the server until the infinite loop is stopped by "page refresh" as soon as I refresh the page the other requests are processes immediately

Why would the Server-Sent Event cause such an issue?

A great resource for the different types of poling can be found in this Question

Community
  • 1
  • 1
Jaylen
  • 39,043
  • 40
  • 128
  • 221

1 Answers1

5

Everything looks robust, so I'm going to take a guess that you are being hit by session locking. PHP sessions lock the session file, such that only one PHP script can use the session at a time; when you think about it, this is a great idea!

The problem with sessions and SSE is that the SSE PHP process runs forever, and therefore it locks the session forever. If any other PHP script tries to run with the same session, it will block (at the session_start() call, I believe).

This looks like a good article on the subject; the advice is to call session_write_close() once you know longer need the session. E.g. if you just need to use the session to check they have previously authorized themselves, then straight after that you call session_write_close(), and other processes will not get blocked.

Darren Cook
  • 27,837
  • 13
  • 117
  • 217
  • 1
    Wow, I am so much thankful for your answer. I think this is the problem. but the issue now is that i will have to put my `session_start()` at the beginning of my loop and `session_write_close()` at the end of my loop which give me another issue `Warning: session_start(): Cannot send session cache limiter - headers already sent` and `Notice: ob_end_flush(): failed to delete and flush buffer. No buffer to delete or flush i` any ideas on how to solve this problem? – Jaylen Jun 16 '15 at 22:36
  • 1
    Yikes, do you really need an active session on every iteration? Couldn't you just save the needed values out of the session into local variables, at the top of the program, and then use them? Or do you need to write to the session, and/or read latest values from it? (The `session_start()` warning is because it is trying to send the session cookie again, and failing; you could use `@` to suppress it, safely.) – Darren Cook Jun 17 '15 at 08:25
  • Unfortunately in my case I have to update a session every second and then read it. It is a cache mechanism for messages received. What I did is disable the error this page which worked as a workaround. Thanks again for your help. If i could give you +100 votes I would have :) – Jaylen Jun 17 '15 at 21:50