note 64525 modified in ref.session by danbrown
| From: | danbrown@php.net | Date: | Sun, 20 Jul 2008 18:17:46 +0000 |
| Subject: | note 64525 modified in ref.session by danbrown | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-142214@lists.php.net to get a copy of this message | ||
Session locking (concurrency) notes:
As mentioned several times throughout this section on Sessions, the default PHP session model locks
a session until the page has finished loading. So if you have two or three frames that load, and
each one uses sessions, they will load one at a time. This is so that only one PHP execution context
has write access to the session at any one time.
Some people work around this by calling session_write_close() as soon as they've finished
writing any data to the $_SESSION - they can continue to read data even after they've called
it. The disadvantage to session_write_close() is that your code still will lock on that first call
to session_start() on any session'ed page, and that you have to sprinkle session_write_close()
everywhere you use sessions, as soon as you can. This is still a very good method, but if your
Session access follows some particular patterns, you may have another way which requires less
modification of your code.
The idea is that if your session code <b>mostly</b> reads from sessions, and rarely
writes to them, then you can allow concurrent access. To prevent completely corrupted session data,
we will lock the session's backing store (tmp files usually) while we write to them. This means
the session is only locked for the brief instant that we are writing to the backing store. However,
this means that if you have two pages loading simultaneously, and both modify the session, the
<i>Last One Wins</i>. Whichever one loads first will get its data overwritten by the one
that loads second. If this is okay with you, you may continue - otherwise, use the
session_write_close method, above.
If you have complicated bits of code that depend on some state in the session, and some state in a
database or text file, or something else - again, you may not want to use this method. When you have
two simultaneous pages running, you might find that one page runs halfway through, modifying your
text file, then the second one runs all the way through, further modifying your text file, then the
first one finishes - and your data might be mangled, or completely lost.
So if you're prepared to debug potentially very, very nasty race conditions, and your access
patterns for your sessions is read-mostly and write-rarely (and not write-dearly), then you can try
the following system.
Copy the example from session_set_save_handler() into your include file, above where you start your
sessions. Modify the session write() method:
<?php
function write($id, $sess_data)
{
global $sess_save_path, $sess_session_name;
$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
flock($fp,LOCK_EX);
$results=fwrite($fp, $sess_data);
flock($fp,LOCK_UN);
return($results);
} else {
return(false);
}
}
?>
You will probably also want to add a GC (Garbage Collection) method for the sessions, as well.
And of course, take this advice with a grain of salt - We currently have it running on our testing
server, and it seems to work OK there, but people have reported terrible problems with the Shared
Memory session handler, and this method may be as unsafe as that.
You can also consider implementing your own locks for scary concurrency-sensitive bits of your code.
--was--
Session locking (concurrency) notes:
As mentioned several times throughout this section on Sessions, the default PHP session model locks
a session until the page has finished loading. So if you have two or three frames that load, and
each one uses sessions, they will load one at a time. This is so that only one PHP execution context
has write access to the session at any one time.
Some people work around this by calling session_write_close() as soon as they've finished
writing any data to the $_SESSION - they can continue to read data even after they've called
it. The disadvantage to session_write_close() is that your code still will lock on that first call
to session_start() on any session'ed page, and that you have to sprinkle session_write_close()
everywhere you use sessions, as soon as you can. This is still a very good method, but if your
Session access follows some particular patterns, you may have another way which requires less
modification of your code.
The idea is that if your session code <b>mostly</b> reads from sessions, and rarely
writes to them, then you can allow concurrent access. To prevent completely corrupted session data,
we will lock the session's backing store (tmp files usually) while we write to them. This means
the session is only locked for the brief instant that we are writing to the backing store. However,
this means that if you have two pages loading simultaneously, and both modify the session, the
<i>Last One Wins</i>. Whichever one loads first will get its data overwritten by the one
that loads second. If this is okay with you, you may continue - otherwise, use the
session_write_close method, above.
If you have complicated bits of code that depend on some state in the session, and some state in a
database or text file, or something else - again, you may not want to use this method. When you have
two simultaneous pages running, you might find that one page runs halfway through, modifying your
text file, then the second one runs all the way through, further modifying your text file, then the
first one finishes - and your data might be mangled, or completely lost.
So if you're prepared to debug potentially very, very nasty race conditions, and your access
patterns for your sessions is read-mostly and write-rarely (and not write-dearly), then you can try
the following system.
Copy the example from session_set_save_handler() into your include file, above where you start your
sessions. Modify the session write() method:
<?
function write($id, $sess_data)
{
global $sess_save_path, $sess_session_name;
$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
flock($fp,LOCK_EX);
$results=fwrite($fp, $sess_data);
flock($fp,LOCK_UN);
return($results);
} else {
return(false);
}
}
?>
You will probably also want to add a GC (Garbage Collection) method for the sessions, as well.
And of course, take this advice with a grain of salt - We currently have it running on our testing
server, and it seems to work OK there, but people have reported terrible problems with the Shared
Memory session handler, and this method may be as unsafe as that.
You can also consider implementing your own locks for scary concurrency-sensitive bits of your code.
http://php.net/manual/en/ref.session.php