During my mock Zend PHP certification exams I have came across the following question that I have researched online but have not found a definitive, and understandable, answer to it. Can You advise what the answers to these may be or point me in the right direction?
Consider 3 PHP files that are called asynchronously via XmlHttpRequest:
// s1.php session_start(); sleep(1);
// s2.php session_start(); sleep(2);
// s3.php session_start(); sleep(3);
Which of the following statements is true? (Choose two.)
A. The total execution time for all 3 requests will be the maximum of the longest sleep() call
B. The requests may be processed out of order
C. The requests are guaranteed to be executed in order
D. Concurrent requests will be blocked until the session lock is released
I think is B and D.
From PHP docs:
PHP's session handler is blocking. When one of
your scripts calls session_start(), any other script that also calls
session_start() with the same session ID will sleep until the first
script closes the session.
A common workaround to this is call session_start() and
session_write_close() each time you want to update the session.
And:
If you need to keep a PHP page running after
opening a SESSION, simply close it when finished writing to
$_SESSION so the AJAX polling page can authenticate and use the same
session in a seperate web page...
session_write_close();
And:
PHP locks the session file until it is closed. If you have 2 scripts
using the same session (i.e. from the same user) then the 2nd script
will not finish its call to session_start() until the first script
finishes execution.
If you have scripts that run for more than a second and users may be
making more than 1 request at a time then it is worth calling
session_write_close() as soon as you've finished writing session data.
Related
session must be synchronized. When A writes/saves to session B or another A is put on wait. Which is a must for file based session handling.
But once A has loaded the session (but now saved its modifications) B should also be allowed to load the same session. because loading will open the file and bring the file contents in memory and close.
Is there any reason to block all other scripts during the whole time A loads session and A saves the session. can't the synchronization be done only with save handler ?
So two PHP scripts can never work concurrently If they share the same session.
for example seslock.php
<?php
header('Content-Type: text/plain');
session_start();
if(isset($_GET['wait'])){
sleep(30);
echo "waiting\n";
}else{
echo "No Waiting\n";
}
?>
done
visit seslock.php will respond immediately but seslock.php?wait will take 30 seconds to respond. But the problem is if you request seslock.php?wait first and only seslock.php second. event the non-sleep block will ask you to wait for 30 seconds too.
Why it block is not my question. I am asking why it blocks start to save ? instead of blocking only save ?
Possible duplicates:
How does session_start lock in PHP?
Why does session_start cause a timeout when one script calls another script using curl
session_start hangs
How to kill a PHP session?
... And many ;)
Edit
The reason why it blocks is because the session file is being read and it might be modified at any point of time when the first script is running, hence the lock.
The remedy to this could be a session_write_close() as pointed by this post
How to prevent blocking php requests, by Konr Ness
The default PHP session handler is made to serialise session changes for each session id. This has the benefit of a guaranteed consistent session state across your scripts.
You can give up this advantage by running session_write_close() right after session_start(). This also makes your session read-only though.
Alternatively you can write your own session handler without locking.
I am buidling some webapp and have implemented long-polling (and a command queue in my db) so my server can send commands to my cleint asynchronously, etc. The commands are encoded into json and sent over ajax calls for the client to server, and via long-polling for the server to client way.
Everything was working just fine, until I included my "Authentication module" in the ajax.php file. This module wraps the session stuff and calls session_start().
The problem is that, my long polling routine can wait up to 21 seconds before comming back to the client. During this time, the server won't run anything from the same session. It's instead executed right after the long polling ajax call returned.
I understand there's probably a restriction of only 1 thread per session at a time, and that the requests are queued up.
Now here's the question : What is the best way to address this? Is there a setting to allow several threads per sessions (3 would be fine, in my case). Or should I just send tell the client what is his SessionID (i have some sessions table in my db, to track which user is connected to which session(s)). The client could then send it along with any ajax calls so authentication module could be bypassed.
On the later option, iam afraid it open's up a bunch of security problems because of eventual session spoofing. I would need to send a "random string" to each session, to make sure you can't spoof too easily, but even then, it's not perfect...
Thanks for your awnsers :)
Nicolas Gauthier
It's a well known issue/fact that PHP locks session files for the duration of their usage in order to prevent race conditions.
If you take a look at the PHP source code, (ext/session/mod_files.c) you can see that the ps_files_open function locks the session file, and ps_files_close unlocks it.
If you call session_start() right at the beginning of your long-running script, and do not explicitly close the session file, it will be locked until the script terminates, where PHP will release all file locks during script shutdown.
While you are not using the session, you should call session_write_close to flush the session data to disk, and release the lock so that your other "threads" can read the data.
I'm sure you can imagine what would happen if the file was not locked.
T1: Open Session
T2: Open Session
...
T2: Write Data
T1: Write Data
The data written by thread 2 will be completely overwritten by thread 1, and at the same time, any data that thread 1 wanted to write out, was not available to thread 2.
From the manuals, I draw that when a php session var is set, it is written to a text file in the session_save_path folder.
I am just curious to find out if this happens as soon as the interpreter hits the line with the session variable or does it ( writing to the text file ) takes place when the PHP interpreter exits processing the file?
For example, if I were to set and update a session variable in two consecutive lines, (as in the example I gave below), does the PHP interpreter saves the files twice back to back?
In other words, which code snippets has the right commenting?
$_SESSION['my_variable']=1; // writes to the session text file
$_SESSION['my_variable']=2; // writes to the session text file again
die(); //
versus
$_SESSION['my_variable']=1; // updates the session file contents in the memory
$_SESSION['my_variable']=2; // updates the session file contents in the memory
die(); // writes to the session text file
Data is written to file if:
session_write_close() is called
the script execution is finished
So provided that you don't do 1), your second assumption is correct.
Writing to the file every time a variable changes would be VERY expensive, because generally speaking accessing and writing to the disk is slow, hence why PHP won't do that. It should be noted however that caching systems such as memcache or redis will store changes as they happen, so it can be a good idea to rely on them when PHP sessions do not suffice in terms of reliability.
The second one, the $_SESSION[...] = ... they just set the value inside your $_SESSION array and die() triggers this function:
session_write_close
(PHP 4 >= 4.0.4, PHP 5)
session_write_close — Write session data and end session
Report a bug Description
void session_write_close ( void ) End the current session and store
session data.
Session data is usually stored after your script terminated without
the need to call session_write_close(), but as session data is locked
to prevent concurrent writes only one script may operate on a session
at any time. When using framesets together with sessions you will
experience the frames loading one by one due to this locking. You can
reduce the time needed to load all the frames by ending the session as
soon as all changes to session variables are done.
http://nl.php.net/manual/en/function.session-write-close.php
So, I tried to test this by doing the following.
test_1.php
session_start();
$_SESSION['my_variable']=1;
sleep(20);
exit;
and
test_2.php
session_start();
var_dump($_SESSION);
Here are the cases I tested:
Executing test_1.php then quickly executing test_2.php.
Result: test_2.php froze until test_1.php finished sleeping.
Executing test_1.php, removing the PHPSESSID cookie and THEN
executing test_2.php.
Result: test_2.php executed right away with no freezing (test_1.php in the meantime was still sleeping) and printed an empty array.
Novice conclusion:
PHP opens a stream to the text file related to that session until the script has finished executing and then it "commits" the changes. (I'm not an experts, so my terms might not be accurate)
Edit: yet another useless test due to not consulting the documentation
but as session data is locked to prevent concurrent writes only one
script may operate on a session at any time
Sessions will be written in cookies or whatever storage you choose only after flushing the setter page. ex:
having pageA and pageB :
setting in pageA :
session["foo"] = "bar";
session["foo"] will be available to pageB only after:
leaving pageA to pageB
or
leaving pageA and refreshing pageB
that considered, the best definition IMHO is the second one.
Happy coding!
session must be synchronized. When A writes/saves to session B or another A is put on wait. Which is a must for file based session handling.
But once A has loaded the session (but now saved its modifications) B should also be allowed to load the same session. because loading will open the file and bring the file contents in memory and close.
Is there any reason to block all other scripts during the whole time A loads session and A saves the session. can't the synchronization be done only with save handler ?
So two PHP scripts can never work concurrently If they share the same session.
for example seslock.php
<?php
header('Content-Type: text/plain');
session_start();
if(isset($_GET['wait'])){
sleep(30);
echo "waiting\n";
}else{
echo "No Waiting\n";
}
?>
done
visit seslock.php will respond immediately but seslock.php?wait will take 30 seconds to respond. But the problem is if you request seslock.php?wait first and only seslock.php second. event the non-sleep block will ask you to wait for 30 seconds too.
Why it block is not my question. I am asking why it blocks start to save ? instead of blocking only save ?
Possible duplicates:
How does session_start lock in PHP?
Why does session_start cause a timeout when one script calls another script using curl
session_start hangs
How to kill a PHP session?
... And many ;)
Edit
The reason why it blocks is because the session file is being read and it might be modified at any point of time when the first script is running, hence the lock.
The remedy to this could be a session_write_close() as pointed by this post
How to prevent blocking php requests, by Konr Ness
The default PHP session handler is made to serialise session changes for each session id. This has the benefit of a guaranteed consistent session state across your scripts.
You can give up this advantage by running session_write_close() right after session_start(). This also makes your session read-only though.
Alternatively you can write your own session handler without locking.
Every time I access data in $_SESSION, Does it immediately update the session file on the disk, or just once when the process goes down? Or every n bytes of data change (flush)?
This question is not necessarily about the specific file session handler, but every handler. (Does every touch in session immediately invoke an I/O of any kind, beside the storing of a normal variable in memory).
As Matt wrote, it writes at the end of script execution by default. You can read about it here in session_write_close()
Session data is usually stored after
your script terminated without the
need to call session_write_close(),
but as session data is locked to
prevent concurrent writes only one
script may operate on a session at any
time. When using framesets together
with sessions you will experience the
frames loading one by one due to this
locking. You can reduce the time
needed to load all the frames by
ending the session as soon as all
changes to session variables are done.
It writes it and the end of the process on my setup. I made a new _ session_ write_method:
public function _session_write_method($id, $sess_data) {
var_dump(file_put_contents('/var/www/public_html/testing.txt', serialize($sess_data)));
return(true);
}
and then:
$_SESSION['foo'] = 'bar';
while(true)
I executed the script,waited a few seconds and then ran 'sudo kill' on the process id. It did not write the serialized data to the file. I ran it again without the infinite loop and I got: int(22) at the very bottom of the page and testing.txt was successfully written to and contained: s:14:"foo|s:3:"bar";";
Depends on the handler. You can write your own handler to make sure it only happens as often as you like if you want to be absolutely sure about the behavior. There are 6 callbacks used to manage session variables. The one called "write" does not have to perform any real I/O and writing the session file could wait until the call to "close". It is an implementation detail that, as I said, depends on the handler.