Wednesday, February 22nd, 2006

Troubles with Asynchronous Ajax Requests and PHP Sessions

Category: Ajax, Articles

Marc Wandschneider (the Chipmunk Ninja), has written about a race condition that he ran into on a recent project using Ajax techniques and PHP on the backend.

He details the issues, and explores his work-around.

Posted by Dion Almaer at 8:54 am
23 Comments

+++--
3.5 rating from 68 votes

23 Comments »

Comments feed TrackBack URI

Nobody can’t stop you from being fool and doing so

Why in the hell would you want to write and read the same variable with different calls ?

Comment by garaged — February 22, 2006

What a pity… I have news too:

Jumping in front of a car may kill you!

Comment by Tom — February 22, 2006

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.

However, what I really suggest doing is policing your ajax calls to make sure they don’t topple over each other. I do this by checking to see if a previous request is still occuring when I make a new request. If that’s the case, I refuse the new action or delay it.

Comment by Taylor — February 22, 2006

Concurrency issues have long since been solved, if you actually use the recommended methods of avoiding them. Any modern book on threading will give you the basics. With PHP it is especially problematic because each session “variable” is just an array index, and the entire session gets read or written at once, you can not access (or lock) just a single session “variable”. But this is hardly a PHP only or AJAX only problem. You would have the same kinds of issues with any series of multiple concurrent requests for the same page (like downloading images on the page that are dynamically generated based on session data).

The main issue here is that there are no built-in concurrency controls across multiple requests in popular server-side programming/scripting languages, so you’d have to roll your own. Things like atomic increment in memcached or get_lock/release_lock in mysql can help. One may have to forgo using PHP’s session handling to get finer grained locks, which means rolling your own session variable storage engine also.

Comment by Andy — February 22, 2006

I also use PHP and Sessions, and ajax. No problems at all. My magic coins?
– initializing the session on the first page you come to
– session_write_close()
– DB backed sessions

Comment by Gilles — February 22, 2006

Why in the hell would you want to write and read the same variable with different calls?

You do, each time you access any variables shared across a set of processes or threads. Few webservers process requests serially. PHP’s lack of support for access control to shared datastructures (such as $_SESSION) is a definite flaw in the langauge, cf. Java’s synchronized keyword or ColdFusion’s <CFLOCK> tag, both of which allow concurrent threads to access shared state without stepping on each other’s toes.

You (and Tom, especially Tom for that ill-informed remark) should read up on concurrency. Web applications tend to be highly concurrent, and nobody developing them should be without, at the very least, a solid grounding in concurrency.

And as Andy noted, this is a problem with most dynamic langauges at the moment, not just PHP. Very few provide proper support for taking read-only and excusive locks on shared state.

Comment by Keith Gaughan — February 22, 2006

@Taylor: PHP uses that session persistence mechanisme because it’s simple and portable, and works well with CGI. However there’s absolutely no reason that the persistence mechanism couldn’t use, say, mmap() to share session state across requests, or (god forbid) even a combination of SysV shared memory and semaphores, though I wouldn’t recommend that.

The real problem is that PHP doesn’t have any locking primitives.

Comment by Keith Gaughan — February 22, 2006

And before I forget, to do that, PHP would also need some extra functionality, specifically a way to set handers for when values are read from or written to $_SESSION.

Comment by Keith Gaughan — February 22, 2006

@Keith: Great response. I totally agree with you. The Zend engine can be extended. A PHP extension can be written to replace the internal Session handling routines. If you are interested, I’d love to have a think tank session with you and see what the implications are. Email me at tdondich@groundworkopensource.com.

Comment by Taylor — February 22, 2006

It might be doable now without having to extend the Zend engine at all… in PHP5, after reading the session, assign _SESSION to be an object with __set and __get methods that perform the concurrency checks. on write, convert the object to an array and write it. PHP5 has some issues currently with storing objects in the session variable so there might be some issues to deal with that still. You’d have to access your session variables as $_SESSION->field rather than $_SESSION[‘field’], so old code would have to be ported, but this is hardly a major hangup.

Comment by Andy — February 22, 2006

@Andy, Keith, and Taylor: the article comments suggest that PHP already ensures only one thread has access to $_SESSION at a time. The first thread that session_start()s gets exclusive access until it finishes or calls session_write_close(). While locked, other threads hault execution at the point they call session_start(). When the lock is released, execution continues in the next script. I can’t find this in the PHP manual, but it should be trivial to verify.

I’d think the easiest way to avoid the situation described is to add to your AJAX class the ability to keep track of requests that write to certain session vars. The client app can then decide if it should allow a 2nd write request to be sent before receiving the response from the 1st.

Comment by Stephen Clay — February 23, 2006

Did you guys see another similar article (Java related)?
http://zk1.sourceforge.net/smalltalks/ajax-challenges/ajax-challenge1.html

Comment by michael — February 23, 2006

Ajax�PHPセッション�競�

リンク: Ajaxian ? Troubles with Asynchronous Ajax Requests and PHP Sessions Ajaxian�サイト�掲載�

Trackback by æ€?考ã?¨ç¿’作 — February 23, 2006

The article comments suggest that PHP already ensures only one thread has access to $_SESSION at a time. The first thread that session_start()s gets exclusive access until it finishes or calls session_write_close(). While locked, other threads hault execution at the point they call session_start(). When the lock is released, execution continues in the next script. I can’t find this in the PHP manual, but it should be trivial to verify.

Nope, it doesn’t. Each request has a seperate copy of the session, which is loaded on session_start() and saved on session_write_close(). No locking occurs; instead, the previous copy of the session is overwritten.

This behaviour is usually sufficient when you’re dealing with people browsing a site. The time between two requests in the same session is almost always great enough that this race condition is never an issue. However, when you’re dealing with a piece of code making simultaneous overlapping requests, the crap hits the fan.

It also has to be said that if PHP did use the highly pessimistic locking scheme you described, the uses of sessions could cause it to run like treacle, with each request tied to a certain session being serialised. You’d run into problems with memory consumption, tied up file handles, request timeouts, &c. It’s not a pleasant scenario.

I’d think the easiest way to avoid the situation described is to add to your AJAX class the ability to keep track of requests that write to certain session vars. The client app can then decide if it should allow a 2nd write request to be sent before receiving the response from the 1st.

Safer might be to batch and/or serialise any such requests on the client. Wrap the XHR object in another object that maintains a queue of all pending requests and a list of any sent and pending completion (for callback). When the XHR object is free, send any pending requests together in one batch. It takes a little extra processing on each side to build and seperate each of the logical requests and responses, but it’s worth it.

Comment by Keith Gaughan — February 24, 2006

@Andy, Keith, and Taylor: the article comments suggest that PHP already ensures only one thread has access to $_SESSION at a time.

That comment is wrong. If you overload the session handler, there is no locking function, which would be necessary in order to fully lock sessions when you have multiple web servers. This would suggest that, while it might do locking if storing sessions in files, no locking can be done when you write your own session handler (like using mysql to store session data, for example).

Comment by Andy — February 25, 2006

[…] 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. […]

Pingback by SitePoint Blogs » AJAX and Session “Race Conditions” — February 27, 2006

Concurrency problems with sessions is not new to ajax apps. Anyone who has built web apps with frames where the frames share backend state will have experienced them before. Just saying “make sure your app doesn’t read & write the same data in different calls” doesn’t make you look very smart; just demonstrates you probably have little real-world experience. One particular real world gotcha is that different browsers load framesets in different ways, and even with locking sessions it can be tricky to iron out all the concurrency issues.

One way to deal with locking sessions is to use db backed sessions and roll your own locking mechanism. You need to use transactions, and in order to handle the occasional high-latency or interrupted request, you’re going to want to set up your locks to expire if another request has been waiting a while. To balance fast response with avoiding having high traffic situations result in an exponential growth of db access, you’ll want to have the sleep period between polls to see if the lock is available grow rapidly and geometrically/exponentially. It’s also wise to have available read-only sessions that don’t acquire a lock and only use locking when it is necessary, to minimize overhead. A request should be able to decide it needs a write log part way through it’s execution and ask for it.

Some databases already have a locking mechanisms that makes building this kind of setup easier.

Comment by Michael Johnston — May 22, 2006

just to clarify the situation here…
php, with it’s standard session module (fs), locks the complete session on every request.
just search in the file ext/session/mod_files.c for:
flock(data->fd, LOCK_EX);
but if you replace the session handler with session_set_save_handler, no locking takes place, so you have to write your own (this is pretty obvious, because if you deploy a distributed session design, there’s no way php can make a network wide lock).
like it’s been suggested, memcached’s increment/decrement/add could be used to ensure the proper locking…..

Comment by plenque — June 11, 2006

it is lun

Comment by lun — June 15, 2006

I’ve quoted this thread and provided a solution for the matter at Ajax Requests and PHP Sessions Solution. Hope it helps everybody.

Comment by Pedro — August 22, 2006

Sadly Pedro’s link no longer works. Any other suggestions for how to deal/code PHP sessions with Ajax requests?

Comment by PaddyPatPat — January 14, 2010

I found an archived version of Pedro’s link here: http://www.streetdirectory.com/travel_guide/156917/programming/ajax_requests_and_php_sessions_solution.html

And really there are quite a few classes at phpclasses.org that might be helpful to you. See: http://www.phpclasses.org/search.html?words=mysql+session and http://www.phpclasses.org/browse/package/1518.html for the specific class Pedro mentions.

Comment by arcodesign — February 11, 2010

If in case the session vars written in the ajax script and used in another page or the landing page, you should use: session_register(”);
and then set the value.. such variables will be then globally available as soon as they are set. This definitely solves the issues from the thread:
http://www.phpfreaks.com/forums/index.php/topic,250223.msg1173027.html#msg1173027

Comment by dhonde — July 30, 2010

Leave a comment

You must be logged in to post a comment.