Concurrent Ajax Requests and PHP

I’d like to start by explaining what Ajax is and how we misuse it.

Ajax is an acronym for Asynchronous JavaScript and XML. And let’s focus on Asynchronous for a minute. This means that we can do non blocking operations while the browser can still do what it wants. And when the result is received from the Ajax request it can process it. We’ve all used it and it works fine. But have you ever tried to create a chat based on Ajax ? I did and I hit a brick wall that I couldn’t pass.

Let me explain how I wanted to create the chat. I wanted to use Comet programming technique to get the new replies and normal Ajax requests to send my lines. So to be exact for the replies of other users I had an Ajax request to the server that responded if there are new replies in the chat or if it reaches 15 seconds. And to send my lines of chat I had a normal Ajax request that sends my line and responds as soon as it can.

what you expect to happen

What I wanted to happen

 

This image should make it more clear on what I expected to happen. But it didn’t. What actually happened is that the long request, the request that waited for new messages blocked all of my requests.

what actually happens

How the requests actually ran

Why did this happen ? Don’t I use Ajax ? Doesn’t Ajax mean asynchronous requests ? Can’t I do concurrent Ajax requests ? Is it a browser limitation ? Is it a server limitation ? What is happening here ?

None of these questions had any response. And I couldn’t find any response for almost a year. I’ve searched for the response everywhere. Ajax is indeed asynchronous I’ve read about limitations in browser requests and it seems that the modern browser don’t have a 2 request limitation ( there’s a higher limitation but this wasn’t the reason my requests were blocked ). The Apache server does not limit to 2 requests per IP. That would be stupid.

I couldn’t find the cause of this behavior until recently. It seems that PHP is the cause of the problem here. Almost all of the PHP applications use file sessions to store data. When you do session_start() the PHP locks the session file so there’s no concurrent writing to this file and gives the running script full access to the session variables ( reading and writing ). When the request ends the session data is written to the file and the file is closed, thus removing the lock.

So what does this mean if we have 2 requests running shortly one after another ? The first request will lock the session file do its thing and close the session file. The second request’s session_start() needs to wait for the lock on the session file to be removed to be able to do anything. So how the requests actually ran for the chat that I implemented make sense right now.

Now that I know the problem, how can I fix it ? First let me explain what I needed concurrent requests for now. In one of my current projects right now I have a very long Ajax request ( more than 2 minutes ) that does lots of operations and I needed to show a progress bar so the user knows how much time it will take. I used a different mechanism to save data, the CodeIgniter’s caching driver to save the progress of the long action and another Ajax request to get the same data and output it to the user. The CodeIgniter’s caching driver doesn’t do file locking until the end of the request. It only locks the file while it’s doing the saving and this takes a very short time.

So this one is fixed. But how did I actually do it ?

I’ve created an example to show you how the session file lock affects the Ajax requests you do. There’s an HTML file that fires the Ajax requests and there’s the PHP file that responds in different manners to show you how different approaches act differently.

The function session_write_close() closes the session file, thus removing the lock, but you cannot write data to the session until you run session_start() again. This not a problem because you can run session_write_close() and session_start() in the same PHP script as much as you like.

The HTML front file

The PHP responder

And here’s a demo to play with containing the 2 files listed above.

And another screencast with this demo. But I do recommend that you play around with the demo and find out for yourself when the request responds and why as well as when it doesn’t respond and why. Try combinations of requests with session without session close and requests with session with closed session and figure it out why it responds the way it does.

I know I should have posted the node.js proxy server this was a problem that was bugging me for a very long time and I just needed to get it out. Next post will be the proxy.

5 thoughts on “Concurrent Ajax Requests and PHP

  1. Rob

    I had a similar problem a while back, and found that you can also change the session handler to use MemCache, or a database to avoid the session file locking problem.

    1. hydrarulz

      I agree. You could change the session handler.

      My initial problem was that I didn’t know what was causing this.

    2. Kristian

      Removing the locks protecting session data is not solution. It just causes race condition where simultaneous requests might end up overwriting each others changes to session data.

      Problem is the operation of $_SESSION itself. The way it works by reading and writing one single lock-protected array between PHP process and storage device does not fit in AJAX concept where there would be multiple concurrent requests running in same session context but doing different things.

      Basically session data needs to be stored in much smaller independent fragments, not as single shared blob like PHP’s builtin session mechanism does. Memcached can do that when used as plain key-value storage, but being a cache there is problems with uncontrolled expiry of stored data. I would suggest trying out SCache. It provides key-value storage with session lifecycle.

  2. Pingback: Challenging edge case for PHP's session reliability | LeaseWeb Labs

Leave a Reply