I've been trying to switch to MySQL-based session storage instead of the native PHP session storage. In doing so, I've run into a lot of code on the web that exhibits a serious flaw regarding concurrent requests from the same client. All the code I've seen has glossed over the need to lock the session for the duration of the request, e.g. not allow concurrent requests to use it until it has finished. PHP's native session handler correctly does this, but lots of MySQL-driven session code does not.
Neither do.
Example timeline:
1. client sends a request
2. session data is loaded from the db 3. same client sends a request before the first one is finished (e.g. frames, tabbed browsing, slow server response times, etc)
4. session data is again loaded from the db
5. the first request changes some values in $_SESSION
6. the second request changes some values in $_SESSION
7. the first request writes the data to the db and exits
8. the second request writes it's data (over that written by the first request) and exits
PHP's native handler solves this problem by forcing concurrent requests to wait for each other. The same thing needs to happen in a database-driven session handler.
No request should be blocking (i.e. wait for concurrent processes to execute). This implies a poor understanding of transactional processing from both yourself and PHP!
SO, does anyone have some code that uses MySQL to replace PHP's native
session storage that also correctly handles this concurrency problem? Ideally I'd like to see just a set of functions that can be used with sessions_set_save_handler() to transparently shift PHP's sessions to a database, but I'm not going to use the stuff I've found on the web or even in books (appendix D of O'Reilly's Web Database Applications with PHP & MySQL publishes code with this problem).
Forget it - rethink how your application is architected.
Folks using database sessions who do not deal with this scenario be warned! I'm surprised so much bad code is going around for this task...
Agree.
Some guidelines, from my experience of developing time-critical transactional systems in .Net (100 transactions/second type load).
- You usually only store some kind of identification for a user in the session - no other data. doing otherwise is dangerous as there are multiple copies of data floating around uncontrolled. A session-id is enough information to store. Don't use the session for storing data willy nilly - it is for identifying the session only - nothing else. Can't say that enough. Don't use it for shortcutting code.
- If you want a proper transactional system, there are two ways to handle concurrency:
1. Block until the transaction is committed - no good for performance and scalability as you spend more time waiting than doing.
2. Fail commit on change. A sample solution: For each row, add an INT which is incremented every time the data is updated (or use TIMESTAMP in MySQL if you have to use it). Read the value with the data and send it every time the data is saved. If the value is the same when it is recieved, then consistency can be guaranteed so update the row, else rollback the transaction (and tell the user someone else changed it before them - reload the view and give them an opportunity to update it again). This requires a good understanding of the DBMS you are using and the principles around ACID compliant databases.
I'd personally recomment PostgreSQL over MySQL as MySQL is not truly atomic and can't do transactions database-side (no server-side programming) which makes it about as scalable as a brick. Small updates/reads yeah but nothing much more complicated.
Personally, no-one in the PHP/MySQL arena tends to understand these concepts as the two technologies are rarely used for pushing data around on big systems (this is generally Java/Postgres' domain).
I ONLY use PHP/MySQL for knocking up quick web apps to fart out content - nothing serious because it's simply not suited.
Cheers,
Chris Smith Ninja Labs http://www.ninjalabs.co.uk/
-- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php