| Jim Gallacher 2005-04-08, 8:45 pm |
| Gregory (Grisha) Trubetskoy wrote:
>
> [also moved this to python-dev]
>
>
> On Fri, 8 Apr 2005, Nicolas Lehuen wrote:
>
>
>
> Yes, there is a lock for the duration of the session. This is to make
> sure that no two requests for the same session are processed at the same
> time (this is a little documented artifact of sessions in other
> environments, e.g. jsp).
>
> The lock 0 is there to serialize access to the DBM. So we have "session
> locks" and "dbm store lock" which serve different purposes.
>
> Since there is a one-to-one correspondence between sessions and files in
> a FileSession, you're right the lock 0 use is probably not appropriate
> (although disk I/O is ultimately serialized by the OS).
>
> However, keep in mind that BaseSession (session locks) is optional (it's
> an __init__ argument), so when session locking is off, you still (and
> especially more so!) need a store lock.
>
> So I guess the bottom line is that FileSession locking is going to be a
> little different :-)
>
> Grisha
Sorry for the long post here - just kind of thinking out loud.
I've been playing with the code Nicolas committed and found the file
locking is not working quite right. I couldn't figure out what was going
wrong until I re-read Grisha's comments.
There is a deadlock when accessing an existing session with session
locking on. A DOS results for that session, and in the worst case a
complete DOS for any connections. The following bash script demonstrates
the effect:
#!/bin/sh
ab -n 1 http://localhost/session_test.py
ab -n 1 -C pysid=723b98c0abf885a97b8bdc8d806b4bd8 \
http://localhost/session_test.py
where pysid is a valid session id.
The first call to the url will succeed, while the second one will fail
with ab timing out. Here is the FileSession program flow as I see it:
sess = FileSession.FileSession.__init__(req,lock=1)
- calls BaseSession.__init__(req,lock=1)
BaseSession.__init__()
- gets the existing session id from the request cookie
- acquires a lock for this session
- registers the unlock_session_cleanup
- calls BaseSession.load()
BaseSession.load()
- calls self.do_load() which is overriden in FileSession
FileSession.do_load()
- attempts to acquire a lock on the session, but is blocked since
session was previously locked in BaseSession.__init__()
- client times out
- unlock_session_cleanup is never run so lock is never released
- this apache thread or child is deadlocked
- access to this session is blocked forever
Call the url enough times for an exisiting session, and apache will
reach MaxClients and refuse additional connections. DOS. oops.
I assume that overriding BaseSession.load() is the best way to change
the locking behaviour and fix the problem. I haven't worked out the
implications of that yet - I guess I need a little more head scratching
time.
On another note, I also wonder if this type of locking problem might
also be a factor in http://issues.apache.org/jira/browse/MODPYTHON-31
Regards,
Jim
|