AJAX and Session “Race Conditions”

Tweet

Via Keith – here’s a problem I’m kicking myself for not covering explicitly in AJAX@localhostTroubles with Asynchronous Ajax Requests and PHP Sessions by Marc Wandschneider.

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.

Now before I go any further – this is not a PHP problem despite the title (I hope the web ring is paying attention)- this is is a feature of HTTP – it’s stateless. The problem is really the blurring of lines AJAX introduces – this goes right to the line between the two kinds of AJAX – is the client or the server managing state?

As Keith points out;

In short, server-side technologies like PHP don’t let you lock access to your session across requests.

I’d broaden that a little – in short, using a stateless protocol like HTTP, any attempt to lock server side resources across requests will always be an ugly and potentially dangerous hack. For example, what if the client suffers a power cut, shortly after locking something?

As an aside, a useful case study is Dokuwiki, which implements a page locking mechanism to prevent issues with two people editing at the same time. It does this using a “lock” file which get’s “touched” with the latest timestamp whenever you begin editing a page or hit preview. If the server doesn’t hear from you for 15 minutes (default), your lock may be transferred to someone else. As it goes, it’s a pretty good implementation, as it will never result in “zombie” locks that require an administrator to clear – I don’t think there’s a better way to do it. But the timing is based on assumptions about a very specific task that end users are performing (editing a wiki page) and 15 minutes is clearly too long for the typical things people are doing with AJAX. I don’t think this approach can be translated to AJAX – it can’t be done in a generic manner.

Muddying the Waters

The conclusion Marc comes to is probably the smartest;

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.

Along with things I tried to raise here, this is then another thing you’ve got to watch out for when writing AJAX apps. It doesn’t have to be a problem – it depends on what you’re doing.

To try to place that is some kind of big picture, it’s something like “Hey our database doesn’t support ACID transactions but if you’re careful, it won’t matter”. Perhaps you can relate to how ridiculous that is – perhaps not (and yeah – I know – <insert well known DB here that didn’t do transactions once upon a time>) .

What’s troublesome is reading the type of responses to this problem, such as those in reply to AJAXian’s coverage of this. In particular the guys that should “get it” clearly don’t;

The thing about PHP is that it is request based, meaning that information storage is not persistent across requests. The session data is still saved/restored upon each request. If you cannot deal with this and need multiple asynchronous requests to a application to access the same dataset (why is beyond me, there’s other ways to code around that), then I suggest going to a J2EE platform.

Go back to Old Kent Road. Do not pass Go. The thing about HTTP is that it is request based – no matter how many EJBs you throw at it!

The problem here is really specific to the HTML++ style apps (Client / SOA, when / if it happens won’t suffer from this, the session being purely client side) – people really need to be aware that the initial ease of “Hello World!”@localhost doesn’t translate to AJAX == easy. I know AJAX enthusiasts don’t want to hear about the problems but with everyone reading from different sheets and others playing “Hear no evil, speak no evil, see no evil”, we’re not progressing the bar about what you can and can’t do with it. Turning that on it’s head, that AJAX is actually harder than it seems leads to a requirement for subject experts – see that as a good thing if you will.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Andi

    Hmm… As far as I know PHP does implement session locking. This caused some problems in DokuWiki some time ago because having multiple requests from same user at the same time isn’t restricted to AJAX. Using an application in a tabbed browser for example or use a PHP dispatcher for images (like in DokuWiki) will cause the exact same behaviour. The real problem is this locking will force all requests to sequencial mode (because they will wait for the session). The answer is to remove the session lock with session_write_close() as soon as possible (DokuWiki does that).

    About creating the page lockfiles in DokuWiki: The current and soon to be released devel version now uses an background AJAX request to refresh the locks as long as your are busy editing the text. This allows shorter default expires for locks. :-)

  • kyberfabrikken

    Rather than using a timeout, we might also implement a feature to let the user decide how to handle the conflict. If you take a look at googles newest experiment Google Page Creator, you can see, that if you open the same document in two separate browser-windows, you’ll get a warning that this document is being edited by another user, and then the option to break the lock.
    I know that this will depend a lot on the context, but for many applications such a solution is probably quite useful.

  • http://shiflett.org/ shiflett

    Unless I’m remembering incorrectly (which is possible), there is session locking. The second request’s call to session_start() should cause a delay if the first request hasn’t completed, because it will wait.

  • http://www.phppatterns.com HarryF

    Hmm… As far as I know PHP does implement session locking.

    But not across multiple requests – this is rather if one incoming request is still being processed by the server, further requests have to wait. Once your request has completed (you got your response and the HTTP connection is closed), you no longer have a lock or any rights over the session.

    As I understood Marcs problem, it’s that you can’t predict the order in which asynchronous requests will arrive at the server and you can’t “lock” (execute a transaction) across multiple requests to prevent the problem. It’s a consequence of what I described on slide 14. And as Marc demonstrates, developers tend to “trust” sessions, being used to their being handled transparently by server side technologies.

  • http://www.phppatterns.com HarryF

    Unless I’m remembering incorrectly (which is possible), there is session locking. The second request’s call to session_start() should cause a delay if the first request hasn’t completed, because it will wait.

    True but that doesn’t help you if Request B arrived at the server before Request A.

  • Ren

    Hmm interesting looking at PHPs source, it does seem PHP file session handler does acquire a exclusive lock on the individual session files.

  • http://shiflett.org/ shiflett

    True but that doesn’t help you if Request B arrived at the server before Request A.

    I’m finding it hard to appreciate the problem. Can you give us a more realistic example of a situation where you need to maintain a particular order? His example is bit contrived, and the explanation doesn’t seem to line up with what’s really happening very well.

  • http://www.phppatterns.com HarryF

    About creating the page lockfiles in DokuWiki: The current and soon to be released devel version now uses an background AJAX request to refresh the locks as long as your are busy editing the text. This allows shorter default expires for locks. :-)

    That’s muddying my argument even further (and it’s a great feature) ;) Where I said this;

    I don’t think this approach can be translated to AJAX—it can’t be done in a generic manner.

    That now needs to be further qualified – doesn’t translate to a generic solution where the primary mechanism for updating something server side is itself done via AJAX. What you’re doing is assisting traditional form posts with AJAX, keeping the lock fresh as a user types.

    Otherwise re-read Marc’s post to see if I can interpret it any other way and I’m completely sure he’s talking about multiple requests arriving at the server out of order. In pseudo code it’s something like this issued by the client;

    
    writeData1('Some value');
    readData();
    writeData2('Some other value');
    
    

    Might be seen in this order on the server;

    
    readData();
    writeData2();
    writeData1();
    
    
  • http://www.phppatterns.com HarryF

    I’m finding it hard to appreciate the problem. Can you give us a more realistic example of a situation where you need to maintain a particular order? His example is bit contrived, and the explanation doesn’t seem to line up with what’s really happening very well.

    OK – this is actually quite hard as much of HTML++ style AJAX is some dubious usability – but off the top of my head (not that I’d personally want to do this), let’s say with a wiki page editor, you want to allow people to “undo/redo” their typing.

    For some reason you decide to use a server side session variable (rather than client side) to keep track of what they’ve done – at various points while they type, you record their actions with a background AJAX request.

    But you fail to consider the effects of latency your AJAX requests, if sent close enough together in time, could arrive at the server out of sequence, meaning the list of user actions, as stored on the server, is different to what they actually did. When they come to “undo”, you pull this information back from the server and all kinds of wierd things start happening because the order of undo operations is wrong.

    So there’s nothing “new” here, that I didn’t cover in AJAX@localhost regarding latency issues (latency varies with time) but the “gotcha” is people tend to trust the $_SESSION store to the point where they don’t think about it.

    At least that’s how I see it. Make some sense?

  • http://shiflett.org/ shiflett

    Make some sense?

    Yes, thanks.

    So, the moral of the story is, if you care about the order in which requests are processed, make sure you do something about it? :-)

  • Ren

    Like using the 3rd parameter on XmlHttpRequest’s open call to not be an async call.

  • http://www.phppatterns.com HarryF

    Like using the 3rd parameter on XmlHttpRequest’s open call to not be an async call.

    Except that you definately need to see this.

  • Ren

    I’m fairly sure there are ways to prevent that. Like calling sync calls from an iframe for instance, so the whole window isn’t locked.

  • http://www.phppatterns.com HarryF

    I’m fairly sure there are ways to prevent that. Like calling async calls from an iframe for instance, so the whole window isn’t locked.

    I’d certainly be interested to hear about it if it is possible. That said it’s worth reading this: Making callbacks (and Atlas) synchronous, or how to shoot yourself in the foot – I’m not just a lone nut grumbling about AJAX ;)

  • Dr Livingston

    > But you fail to consider the effects of latency your AJAX requests, if
    > sent close enough together in time, could arrive at the server out of
    > sequence, meaning the list of user actions, as stored on the server, is
    > different to what they actually did.

    that could be a problem but couldn’t you for example, delay or even prevent further Ajax intervention until the client side has had confirmation that the request sent via Ajax was actually performed on the server side?

    so you send one request to update the server side first of all as the user does the edit, then send another request to verify that the previous request was successful or not;

    if successful then repeat the client side request at appropriate intervals as you would, else alert the user that there was a problem with the update; you allow the user then to manually do the update themselves, which when they do so, the automatic update is paused, no?

  • Joshua Eichorn

    The problem here is really specific to the HTML++ style apps

    I don’t think this statement is true. Even on an SOA app the server has too keep some minimal state around. In many apps this will just be authentication information so it doesn’t cause much of a problem. You can’t trust a your client, and you don’t want to have to send tons of state back to the server on each request so the server is going to need to store some things.

    Anyhow I just wrote an example SOA app for my AJAX book and the one thing I noticed that its easy to keep the server stupid on a simple SOA app. But as you add more functionality the server needs to be smarter and smarter to optomize things, otherwise you’ll just end up with a client that has click and wait of a normal web app (or worse) and fancier loading messages.

  • Joshua Eichorn

    Just a note the solution is not sync calls, it will never be sync calls.

    With any sort of decent AJAX library you can easily add support to keep your async calls ordered. Switching to sync will just leave you with a locked UI and higher latency.

    HTML_AJAX already has support for keeping AJAX reponses ordered (http://blog.joshuaeichorn.com/archives/2006/02/13/ajax-and-latency/), and if anyone has a reasonable test case, I can add support for ordering requests too the server as well (though its going to take some client side retry code to make it robust.

  • Ren

    I may have been too hasty with the “ways to prevent that”, some experiements so far havent been too favourable :)

    Another off the top of head idea, is take a leaf out of TCP/IPs book, and use ajax request sequence numbers (like packet sequence numbers) if requests come out of order, then server could wait until preceding packets arrive.

  • Ren

    Or server could reply couldn’t perform request 5, because missing requests 3 & 4, and client could re-send them.

  • ajking

    Excellent, everybody is in agreement (with Chris Shifflet): if you care about the order in which requests are processed, make sure you do something about it? :-)

  • Pingback: Luckymonkey » AJAX - pushing JS to places you don’t want to go?

  • wei

    If you only care about the latests request/response, you can drop late requests/responses using a counter, 1 keep by the client for responses, 1 keep by the server for requests. If you care about all responses/requests, don’t use AJAX.

  • Pingback: Ajaxian » AJAX and Session “Race Conditions”

  • elmer

    would keeping a cookie counter that gets updated before each request help? if the server receives out of order reuests it would buffer and process in-order or time-out as the network stack does? I can see a mild problem when the user branches a web.app in the same browser…

  • ringobob

    Well, my AJAX is kind of ready made to handle that… I’ve experienced issues with a single instance of IE returning cached results if I access the same URI, without re-accessing the script. So, I just pop a timestamp on the end of each query string in order to differentiate it and force IE to call the script. For the record, the type of app this has given me issues with is the kind of hybrid HTML++ – Client/SOA much like the script.aculo.us shopping cart you linked to in one of the linked articles.

    If this is evidence of some greater deficiency in my code, I’d be happy to hear it, I’m not fond of hacks like this (meaningless querystring variables), but that was the only solution I could find in ~15 minutes of Googling.

    Regardless, it would be trivial to use this to keep track of call order.

  • ringobob

    for example (sorry for the second post), keep a stack of requests, ordered by the submitted timestamp, and just compare the current request submit time to the top of the stack, if it’s newer execute as normal, if it’s older fit it in the stack and act appropriately (e.g. re-execute from current request to the end, don’t execute at all, warn user, etc.).

    All of this assumes that you won’t be sending requests quicker than 1/millisecond, at least not with Date’s getTime(). I’d guess that’s a pretty reasonable assumption in most cases.