Server-Sent Events Polling causing long delays

1.6k views Asked by At

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

1

There are 1 answers

3
Darren Cook On BEST ANSWER

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.