Ninjas are deadly. Chipmunk Ninjas are just weird.
About this blog
Marc Travels
Marc on Twitter
JustLooking on Twitter

Marc Wandschneider is a professional software developer with well over fifteen years of industry experience (yes, he really is that old). He travels the globe working on interesting projects and gives talks at conferences and trade shows whenever possible.

My Publications:

My book, "Core Web Application Programming with PHP and MySQL" is now available everywhere, including

My "PHP and MySQL LiveLessons" DVD Series has just been published by Prentice-Hall, and can be purchased on Amazon, through Informit, or Safari

Feb 21, 2006 | 12:11:37
Troubles with Asynchronous Ajax Requests and PHP Sessions
By marcwan

As I sit here watching “The Muppets Take Manhattan” in Spanish in the middle of a Costa Rican thunderstorm, I find my mind drifting back to a recent project where I spent a day debugging a frustratingly annoying problem: A user would visit the web application I was working on, and after a given page was loaded, all of the session data associated with their visit would be suddenly gone. The user would no longer be logged into the site, and any changes they made (which were logged in session data) were lost.

I spent tonnes of time in the debugger (while at times unreliable and frustrating on huge projects, the Zend debugger is still an invaluable aid for the PHP application developer) and kept seeing the same thing: the session data were simply being erased at some point, and the storage in the database would register ’’ as the data for the session.

It was driving me crazy. I would sit there in the debugger and go through the same sequence each time:

  • Debug the request for the page load.
  • Debug the request for the first Ajax request that the page load fired off.
  • Debug the request for the second Ajax request that the page load simultaneously fired off.
  • Debug the request for the third Ajax request that the page initiated

In retrospect, looking at the above list, it seems blindingly obvious what I had been running into, but it was very late in a very long contract, and I blame the fatigue for missing what now seems patently obvious: A race condition.

For those unfamiliar with what exactly this is, a race condition is seen most often in applications involving multiple “threads of execution” – which include either separate processes or threads within a process – when two of these threads (which are theoretically executing at the same time) try to modify the same piece of data.

If two threads of execution that are executing more or less simultaneously (but never in exactly the same way, because of CPU load, other processes, and chance) try to write to the same variable or data storage location, the value of that storage location depends on which thread got there first. Given that it is impossible to predict which one got there first, you end up not knowing the value of the variable after the threads of execution are finished (in effect, “the last one to write, wins”) (see Figure 1).

Racing to destroy values

Normally, when you write web applications in PHP, this is really not an issue, as each page request gets their own execution environment, and a user is only visiting one page at a time. Each page request coming from a particular user arrives more or less sequentially and shares no data with other page requests.

Ajax changes all of this, however: suddenly, one page visit can result in a number of simultaneous requests to the server. While the separate PHP processes cannot directly share data, a solution with which most PHP programmers are familiar exists to get around this problem: Sessions. The session data that the various requests want to modify are now susceptible to being overwritten by other ones with bad data after a given request thinks it has written out updated and correct data (See Figure 2).

When requests go bad - clobbering data

In the web application I was working on, all of the Ajax requests were being routed through the same code that called session_start() and implicitly session_write_close() (when PHP ends and there is a running session, this function is called). One of the Ajax requests would, however, set some session data to help the application “remember” which data the user was browsing. Depending on the order in which the various requests were processed by the server, sometimes those data would overwrite other session data and the user data would be “forgotten”.

As an example of this problem, consider the following example page, which when fully loaded, will execute two asynchronous Ajax requests to the server.

// race1.php
// part 1

// We will prime the session data with a simple value here.
// One of the Ajax requests going to race2.php will clobber
// this value periodically.
$_SESSION['fudgecicle'] = 'eeeek!';

  <title>Race Condition Demo Page</title>

/* part 2 */
 * getNewHTTPObject
 * This function is here just to create a new
 * XmlHttpRequest object.
function getNewHTTPObject()
    var xmlhttp;

    /** Special IE only code ... */
      @if (@_jscript_version >= 5)
              xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
          catch (e)
                  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
              catch (E)
                  xmlhttp = false;
         xmlhttp = false;
    @end @*/

    /** Every other browser on the planet */
    if (!xmlhttp && typeof XMLHttpRequest != 'undefined')
            xmlhttp = new XMLHttpRequest();
        catch (e)
            xmlhttp = false;

    return xmlhttp;

 * onLoadFunction
 * We call this function when the page loads, and it starts
 * two asynchronous Ajax requests to race2.php.
var xml1;
var xml2;
function onLoadFunction()
    xml1 = getNewHTTPObject();
    xml2 = getNewHTTPObject();'GET', 'http://localhost/race2.php?req1', true);'GET', 'http://localhost/race2.php?req2', true);

    xml1.onreadystatechange = handleResponse1;
    xml2.onreadystatechange = handleResponse2;


 * handleResponse1
 * This handles the response from the first ajax request.
 * It puts the returned string in the <div> element
 * with th eid 'fudgecicles'
function handleResponse1()
    if (xml1.readyState == 4)
        document.getElementById('fudgecicles').innerHTML =

 * handleResponse2
 * This handles the response from the second ajax request.
 * We don't actually care about this, so we just ignore
 * the results.
function handleResponse2()
    // we don't care about this response

     part 3

     This page just executes the onLoadFunction() call and
     contains a single <div> where the result will be
<body onLoad='onLoadFunction();'>
  <div id='fudgecicles'>Waiting for response from Server</div>

The code is divided into three main sections:

  • The first contains the call to session_start() and opens the HTML headers.
  • The second contains the Javascript code to execute the asynchronous requests to the server. The biggest function, getNewHTTPObject() is used to create new objects. The onLoadFunction() is executed when the page finishes loading and starts the ball rolling, while the other two functions are simply used to wait for and handle the responses and results from the asynchronous requests.
  • In the final section, we just write out the <body> section of the document, which contains a single <div> element to hold the results and an attribute on the <body> element to make sure that the onLoadFunction() is called when the document finishes loading.

The asynchronous Ajax requests are then made to race2.php and are processed by the following code, which can handle two different Ajax work requests:

// race2.php


// if they ask for req1, then just return the current value
// of the $_SESSION['fudgecicles'] data.  If they ask for
// req2, then have this session simulate a bogus value by
//  setting $_SESSION['fudgecicles'] to ''.
// NOTE:  the two for loops are to simulate the various
//        Ajax processes doing lots of stuff, and force
//        the processor to interrupt the processes
//        periodically to let the other work.  This helps
//        the race condition occur more.
if (isset($_GET) and isset($_GET['req1']))
    // waste time
    for ($x = 0; $x < 10000; $x++)
        strpos('asdfasdf', 'tttttttttttttttasdfioap');

    if (isset($_SESSION['fudgecicle'])
        and $_SESSION['fudgecicle'] != '')
        $retString = $_SESSION['fudgecicle'];
        $retString = '(empty)';
else if (isset($_GET) and isset($_GET['req2']))
    // waste time
    for ($x = 0; $x < 10000; $x++)
        strpos('asdfasdf', 'tttttttttttttttasdfioap');

    $retString = '';
    $_SESSION['fudgecicle'] = '';

header('Content-Type: text/plain');
echo $retString;

This PHP script handles the two request types differently, and creates the race condition by having the second request type req1 set the sesion data to ’’. (In a real world application, you might have accidentally had this request set some value you thought was meaningful).

If you install the two files race1.php and race2.php on your server, and then load race1.php into your borwser, you will periodically see that the test string is set after the page is completely loaded, and other times it will be “(empty)”, indicating that the second Ajax request has clobbered the value.

Now that we are aware of this problem and how it can manifest itself, the next question is, of course, how do we solve it? Unfortunately, I think this is one of those problems best solved by avoiding it. Building in logic and other things into our web application to lock the threads of execution (i.e. individual requests) would be prohibitively expensive and eliminate much of the fun and many of the benefits of asynchronous requests via Ajax. Instead, we will avoid modifying session data when we are executing multiple session requests.

Please note that this is much more specific than saying simply that we will avoid modifying session data during any Ajax request. Indeed, this would be a disaster: In a Web 2.0 application, we are mostly likely using Ajax for form submission and updating the state of the user data (i.e. session data) as the data are processed and we are responding to the changes. However, for those requests we are using to update parts of pages dynamically, we should be careful to avoid modifying the session data in these, or at least do so in a way that none of the other requests are going to see changes in their results depending on these session data.

Ajax requests and session data do not have be problematic when used together: With a little bit of care and attention, we can write web applications that are powerful, dynamic, and not plagued by race condition-type bugs.

Got other suggestions or solutions to this problem? Add a comment or mail me, and I’ll update the article.

"fatigue" from lack of sleep()?
Posted By: Joe Grossberg Feb 22, 2006 13:57:37
// waste time
for ($x = 0; $x < 10000; $x++)
strpos('asdfasdf', 'tttttttttttttttasdfioap');

Umm ... you might want to look at this instead:
This is not a PHP Problem
Posted By: Pure-PHP Feb 27, 2006 05:01:45
Well this has nothing to do with php, and this is not a php problem. With Java or any other language you would have the same problem.
AJAX noob
Posted By: Lau Mar 09, 2006 03:23:33
I'm a complete newbie to AJAX. I tried to run the script on my webserver, but only got the "Waiting for response from Server" state. Do I need to install a special module or library in order to make use of AJAX, or doesn't this script run at all if I simply copy-paste it in race1.php and race2.php?

Answer through mail are very welcome, please send to lau [at] lazylau [dot] nl

Thnx a bunch!
I have faced the exact same problem
Posted By: Bleizig Apr 05, 2006 05:45:41
I have faced the exact same problem.

I built a firefox extension which was making two requests as it was loading:
- one to automatically login the user
- one to get some data

The second request was repeated every min.

The two first requests were made asynchronously (of course it was an error). What I noticed is that sometimes the login, even though it would pass, would not save anything in the session, making it look like that the auto login never passed. All the subsequent calls to load the data would return the same: "user not logged in".

So, there is indeed a way to have session data lost using two asynchronous calls.

Thanks for confirming why I was suspecting.

So what's the solution?
Posted By: brad_mn1988 Apr 12, 2006 13:01:48
Lots of great stuff here, even though it seems like folks get hammered for ignorance here; I can take it :-)

So what is the solution? Poking around in the manual I had tried session_write_close immediately before the iframe code, but it didn't exactly work.

The iframe consistently blanks out when I trie to call session_start at all.

The sleep() function also looks hopeful, but where to you place it, for how long, and how does it help manage things exactly? Do you just assume that both requests will take x seconds, and if you sleep one of them for x seconds then it will happen last?

Also, Jamie (be nice Jamie, you're clearly smarter than me and I am okay with that, don't be angry that I don't know what you know) says, and it seems like a program as smart as PHP would do this, that PHP only lets one thing at a time handle the sesssion data, so how do I write my code so that the asynchronous requests take turns in the order I want?

What Jamie says seems to contradict the original assertion/assumption that the collision problem only occurs when data is being written, however within the iframe I only read. So I guess I am with Jamie there, but just by experience -- Jamie is right, right?, that it is accessing the session data at all, not just writing to it?

What I am trying to do is pretty pedestrian I am sure, but it seems like it should be a good use of iframes where I can have a scrolling table (within the iframe) of available application links, and which links display depend on session data set upon logon in the parent.

Alright, I poked around again before posting this, and I stumbled upon something interesting: I included the classic version of the magic __autoload function:
function __autoload($class){
}//END __autoload

within my iframe child, and that seemed to hose up the session data -- they would seem to have nothing to do with eachother, but when I disable the autoload the session data is there magically.

I guess I will have to include my classes explicitly within the iframe...

As I am obvoiusly not afraid of looking stupid, I will post this anyway... maybe something in my rambling will inspire a post that will help me or others.

Other than the 'black-box' evidence that autoload got in the way, the session_close_write() before calling the iframe seemed to work, but I won't really know without going back a few steps back into error-ville, not a trip I want to take.

Thanks to all!
A Note on the Comments
Posted By: marcwan May 30, 2006 06:17:20
Thanks to the various users for the generally positive comments.

The point of the original post was to indeed point out that this "race condition" largely occurs because of bad planning or pilot error. PHP has, indeed, properly secured session data writes so that there are no problems with those overwriting each other or causing problems.

However, most PHP application authors have not spent much time in the past thinking about how to deal with asynchronous requests dealing with the same data in non-predictable sequences.

Thus, I wrote this article to point this problem out, demonstrate how easy it is to get, and warn people to avoid it.

In short, you can easily do this, by avoiding having more than one asynchronous Ajax request to the server accessing session data.

MySQL Session Solution
Posted By: Pedro Aug 22, 2006 18:07:54
I've written a short article on this matter and provided another solution for it at
A combination of techniques
Posted By: upNorth Sep 06, 2006 10:06:04
Thanks for comments in this thread for helping lead me towards a solution. To summarize my reading of the results on this (and other) pages:

1. PHP reads a copy of the session variables when you call session_start(). If multiple requests are received that modify the session state, it is likely that the session contents will become unpredictable.

2. To force PHP to write your changes to the session as quickly as possible, call session_write_close() immediately after changing a session variable. This doesn't solve the problem but it does help by reducing the probability that the session's state will be different when read by different, near-simultaneous requests.

3. A stronger solution is to use a database backend for your sessions, and to use transactions on the database to preserve ACID complince. (Note that simply throwing your session into a database makes no difference.) This can be done in MySQL using the InnoDB engine; see the MySQL manual for information on transactions. This is still insufficient if your application must keep track of the order in which requests are issued.

4. The general solution is to maintain, on the client side, a stack which packages the asynchronous requests with sufficient context/timestamp information so that their order can be reconstructed on the server-side before processing occurs. This is a very heavyweight solution and might suggest a gentle re-examination of your application design.

In my environment, we were already using database-backed sessions, so I only needed to implement transactions on the DB calls.

Another Solution
Posted By: dave Nov 19, 2006 21:14:28
I've found that "daisy-chaining" ajax calls can help a lot. For example:

function ajaxFunction1()

function ajaxFunction2()

if (xmlObj.readyState == 4)
//Do whatever you need to do
setTimeout("ajaxFunction2", waittime);

I'm not sure if there is a "finished" readyState (haven't read up on it). If so, possibly nesting the function in a condition of that nature would be better, but I find that this simple solution solves all the problems I have had.
Posted By: Ryan Feb 05, 2007 11:36:37
Great article. Unfortunately, there is a type in the second diagram ("sessionprobs2.png"). In the lower right box, the variable $_SESSION is misspelled as $_SESSSION (Whit three S's in the middle).
A solution
Posted By: darren Edwards Mar 13, 2007 07:25:10
Okay so there are many different cases of bad planning here. Not to worry.

I recently came across this problem where sessions were being destroyed whilst posting data using Ajax. Solution was to use a secondary session system. In my case i used a database which was used to hold data for the user identified by a simple md5 string. This was set up upon the initial page load. All subsequent call to the server used the database data instead of the session data.
By using carefull planning of sequence etc I avoided the 'race condiditon' mentioned earlier.

Why would you want to send two off at the same time on page load anyway. Surley as part of your security procedure any page load would need to be verified before sending to the browser. So log in and get the users details before the page loads.

The best deterent is planning.

Think on how you want the user experience to be. Plan it on paper. In your head. make the least amount of ajax calls that you can. Think of ways to make the calls return more information that may be of use elsewhere. Thus eliminating another call later on.

If it aint' there, you can't have it.

Nice and easy solution
Posted By: Nicolas Quirin Apr 25, 2007 01:04:34
I have indentified recently the same problem of concurrent access from two ajax requests to the same php object stored in a php user session.

So I have decided to use these functions :

$shm = ftok(__FILE__, 'c');
$mutex = sem_get($shm);

I have added to my own Session php class these functions:
private static function lock($object_name_in_session)
private static function unlock($object_name_in_session)
public static getObject($object_name_in_session)
public static writeObject($object_name_in_session)

So, when a script gets an object from the session, others scripts trying to get the same object are blocked until the object consumer script release it!

You could customize the code to protect all the session or only object per object in the session.

I can post a complete php class...perhaps...asap!

Nicolas Quirin
About the above
Posted By: BDKR Apr 30, 2007 11:17:53
I agree that using semaphores is a good server side solution if you are not using a database for session storage, but that approach also seems kind of after that fact doesn't it?

I suspect that the various solutions offered prolly fall into two seperate categories.

1) Someone that hasn't yet encountered the problem (like me) but is aware of it and trying to do what is needed to avoid it.

2) Someone that is trying to come up with a fix for an existing instance of this problem.

The interesting thing about the semaphore approach is that it makes the application seem even more like a multi-threaded event driven desktop app under the hood.

BUT(!), you have to admit that a client side request queue could go a long ways to deal with the issue right? All the way up until you realize the fact that a lot of users are likely to open more the one page on your site, each of which is firing off it's own requests.

wowo, great articel
Posted By: echizen May 29, 2007 12:08:06
its a great artikel to use as reference for ajax n PHP
Iam giving a small solution for not expiring sessions
Posted By: Kalyan Aug 22, 2007 23:50:17
Hi guys ,
Iam giving a small solution to this sessions problem , I dont know am i correct or not also. If iam wrong please give me suggestions.
Just I faced a problem iam using lightbox. Normally it has to raise a http request for logging in before going to innerpage. There iam creating sessions if he is a valid user .but sessions are not getting expired after coming out of request. for tht just i used to save session using session_set_save_handler. by using this i created functions script is below. this is code i have seen from

function open($save_path, $session_name)
global $sess_save_path;

$sess_save_path = $save_path;

function close()

function read($id)
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
return (string) @file_get_contents($sess_file);

function write($id, $sess_data)
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
$return = fwrite($fp, $sess_data);
return $return;
} else {


function destroy($id)
global $sess_save_path;

$sess_file = "$sess_save_path/sess_$id";

function gc($maxlifetime)
global $sess_save_path;

foreach (glob("$sess_save_path/sess_*") as $filename) {
if (filemtime($filename) + $maxlifetime < time()) {
return true;

session_set_save_handler("open", "close", "read", "write", "destroy", "gc");


// proceed to use sessions normally

easier fix
Posted By: backdraft Oct 04, 2007 03:04:51
You can just use cookie and there's no trouble anymore. I do that in my site i use a lot of ajax and there's no problem
Same problem?
Posted By: JeanMarie Oct 19, 2007 02:51:02
I think i have a similar problem with PHP-Sessions.

My situation is that a user logged in to application_A will be automatically logged in to application_B (singlesign on).

Therefor i created a SOAP-Service on application_B side. application_A request via SOAP client the login. Application_B authenticates the request and creates a new session and stores data into it. Then application_B returns a URL and the session ID to application_B. The URL resides with application_B and points to the login script.

Now application_B runs header('Location: <URL>?<session ID>) and will be redirected to the login script of application_B.

The login script expects a session ID. With the session ID the tries to start the according session. This works fine, but the session contains no data.

When i check the session in the filesystem before application_B runs session_start(), the session file contains date (size > 0). When i run session_start() followed by a dump of $_SESSION, the session variable is empty.

I'm confused. I have the feeling session_start() starts a new session with the given session ID. After the session_start() i directly run session_write_close(). Then i check the session file in the filesystem and voila - it's empty (size = 0).

// session file size > 0 (= session contains data)
session_id(<session id from $_GET>);
// session file size = 0 (= session is empty)

Any idea?

Best regards
php5 article code
Posted By: ├želik kap─▒ Jan 22, 2008 06:31:16
plase php5 code samples using php mysql application
Very helpful
Posted By: Adam Mar 20, 2008 14:19:48
Thanks for putting this up, it helped me realize what I was doing wrong. Hope you're making $$ on it
PHP locking solution
Posted By: Bo-oz Apr 07, 2008 04:56:34
I always use session locking... I'm building a ajaxified webshop right now, which allows multiple simultanious requests over Ajax. Each seperate request needs to wait before an earlier request is finished:


while(true) {
if(!$_SESSION['locked']) {
$_SESSION['locked'] = true;


$_SESSION['locked'] = false;
In production environment
Posted By: NETDOOR.FR Jun 20, 2008 06:55:16

while(true) {
if(!$_SESSION['locked']) {
$_SESSION['locked'] = true;


$_SESSION['locked'] = false;

You're sleeping 1000 ms although your session has been's a waste of time, a waste of ressource (memory, cpu) since 'sleep' are very often avoided in a production environment.

I have found a better solution for PHP session with a custom database handler for MySQL.

You have to use innodb engine for your MySQL table.

The only thing you have to do is to add 'LOCK IN SHARE MODE' after a select statment in your custom session handler.

Your php scripts will be blocked until the first script has finished to update the current more wasted time.

Let's MySQL do everything for you.

A solution is found
Posted By: the kolRn Aug 06, 2008 23:43:38
I've just came across the same problem. I'm using prototype js library and it's always worked fine for me. I used to have requests to database using AJAX, and I also wrote and read from sessions (PHP + mySQL + AJAX).

Here is an example:

add2basket = function() {
var url = 'myscript.php';
var params = 'ajax=true';
var ajax = new Ajax.Request(url, {
method: 'get',
onComplete: function(transport) {
onFailure: function() {alert('something is wrong');},
parameters: params


if (isset($_SESSION['myVar'])) {
echo $_SESSION['myVar']'

if (isset($_GET['ajax'])) {


The problem was that when AJAX request is complete the current session myVar value is alerted, but that value is not always incremented.

I tried to use database instead of sessions, thought it would work fine, but... I got the same problem. And finally I found the solution - I used 'post' method instead of 'get'. I thought that when I pass data using 'get' request it can be cached and that is why it works every other time. Using 'post' solved the problem, so try it out, hope it helps you.
session problem with combo
Posted By: vabuk Sep 05, 2008 21:00:58
I have used ajax request to set session when user change the option in a combo. But the problem is that it sometimes works fine and sometimes it doesn't work. Can anybody tell me the reason

Do you really need AJAX?
Posted By: Phil Mar 08, 2009 05:44:47
AJAX is great, but it shouldn't be used just for the hell of it. Others have mentioned in the comments things such as performing requests on page load and auto login scripts.

If you need to update some content on page load, why do you need to make 2 separate requests to the web server in what could be done in one, it would me more efficient and take up less server resources.

If your performing an auto login, then the Javascript persumably reads a cookie and then makes an additional request to the webserver. This again seams unnecessary because you could simply do the login in your server side script as cookies are passed to the server anyway.

It seams like AJAX in some cases is over used when it's not nescessary, I don't see any other reason for this other than it's 'cool to use AJAX'.

Obviously in many cases it is required to perform certain tasks without a full page load but in cases it's just inefficient code and additional load on the web server. On small sites sure this is no big deal, but on a large busy site that uses a cluster of several web servers you could potentially require additional servers just to handle these extra unnescessary AJAX calls.

Back to the question in hand, someone mentioned daisy chaining requests. This is a very effective method which I've used myself for a site which required many AJAX requests to reload a number of different areas of the page in different intervals. In some browser you are also limited to 2 symultaneous requests and having more can cause some very unpredictable results, this was also a great way to get round this problem.
PHP Sessions
A user would visit the web application I was working on, and after a given page was loaded, all of the session data associated with their visit would be suddenly gone. The user would no longer be logged into the site, and any changes they made (which were logged in session data) were lost.Ajax changes all of this, however: suddenly, one page visit can result in a number of simultaneous requests to the server. While the separate PHP processes cannot directly share data, a solution with which most PHP programmers are familiar exists to get around this problem: Sessions.
Another solution
Posted By: Joao Oct 27, 2010 11:35:51
You can make several instances of the getNewHTTPObject object...
I faced the same problem, but I figured out quickly that was a race problem...
If we were using threads in Linux C, we could use "Trafic Lights"... Javascript is too low level and basic to make this high tech things...

So what I going to try is to create an array of intances, creating this ways some kind of threads...

Something like this:

// We got one
try {
ajaxArray[index] = new XMLHttpRequest();
catch(e) {
try {
ajaxArray[index] = new ActiveXObject("Msxml2.XMLHTTP");
catch (e2) {
try {
ajaxArray[index] = new ActiveXObject("Microsoft.XMLHTTP");
catch (e) {
// no AJAX support

if (ajaxArray[index]) {
params = "addr="+serverAddress;
ajaxArray[index].open('POST', 'server_check_ajax.php', true);
ajaxArray[index].onreadystatechange = handleResponse;
ajaxArray[index].setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajaxArray[index].setRequestHeader('Content-Length', params.length);

Lets see if this works...
Mutexes are your friends
Posted By: Oli Comber Jun 04, 2011 18:29:59
Hi guys,

This is a classic problem in multithreading, and there are classic solutions.

General Problem: You have some data shared between many threads of execution. You need to make sure only one thread can change values at a time, otherwise those values will end up corrupted - half changed by one and half by another - or perhaps deleted/clobbered.

Specific problem: A page load or AJAX request loads your session data from the server. In the meantime a page load or another AJAX request loads your session data. The first "thread" finishes during execution of the second, saving some new session data. Then the second thread finishes without making session changes, but resaves the old session data. Result: You have lost your session updates.

If you are using MySQL (Or any DBMS) for sessions, you can't row-lock the session without starting a transaction, and then you're going to tie yourself in knots if you need to use/create transactions within this 'thread'. Locking your whole session table is going to slow EVERYONE down, so you definitely don't want to do this.

There is a solution: Use your own locks in your session handler.

Let's look at a sample session table:

Id char(32), IP varchar(15), Created int, KeepAlive int, Data blob, Locked int default 0

The 'Locked' column is going to tell us if we can access this session or not. To 'Lock' this session, we set this to the current unix timestamp. To unlock it, we set to 0.

The aim here is to serialise access to a single session, such that all the time you have a session open/read, nothing else can access it. When we close_write the session, we can unlock it. Also, we want to avoid stale locks and deadlock.

Some pseudo code:

sessionRead(ID, IP)
session_lock(ID, IP)
return read_session_data(ID,IP)

sessionWrite(ID, IP, DATA)
write_session_data(ID, IP, DATA)
session_unlock(ID, IP)

session_lock(ID, IP)
while true
lVal = get_lock_val(ID,IP)
if lVal == 0 then break
if lVal+5 < time() then break
set_lock_value(ID, IP, time())

session_unlock(ID, IP)
set_lock_value(ID, IP, 0)

So, what we do here is just BEFORE READ, LOCK the session.

Inside LOCK, we keep looping and getting the lock value until it is 0 or is older than 5 seconds - 5 seconds being a value chosen to be longer than what we think our maximum run time might be. This avoids deadlock, though with a small risk. Each loop, we sleep for 0.3 secs.

Inside UNLOCK, we simply set the lock value to 0.

In this way, all access to the same session are safely serialised, but access to different sessions still happens concurrently. No code changes need to happen to your AJAX (And you can't avoid race conditions from the client side anyway), and you are safe to have as many AJAX threads running concurrently as you need.

Hope this is useful.

Add a Comment





Copyright © 2005-2008 Marc Wandschneider All Rights Reserved.