Author: chrisz
Date: Fri Mar 4 02:54:45 2011
New Revision: 8156
Log:
Added an application setting to avoid saving unaltered sessions,
as suggested by Steve Schwarz.
Modified:
Webware/trunk/WebKit/Application.py
Webware/trunk/WebKit/Configs/Application.config
Webware/trunk/WebKit/Docs/Configuration.txt
Webware/trunk/WebKit/Session.py
Webware/trunk/WebKit/SessionDynamicStore.py
Webware/trunk/WebKit/SessionFileStore.py
Webware/trunk/WebKit/SessionMemcachedStore.py
Webware/trunk/WebKit/SessionMemoryStore.py
Webware/trunk/WebKit/SessionShelveStore.py
Webware/trunk/WebKit/SessionStore.py
Modified: Webware/trunk/WebKit/Application.py
==============================================================================
--- Webware/trunk/WebKit/Application.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/Application.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -52,6 +52,7 @@
SessionTimeout = 60,
SessionPrefix = '',
SessionName = '_SID_',
+ AlwaysSaveSessions = True,
IgnoreInvalidSession = True,
UseAutomaticPathSessions = False,
UseCookieSessions = True,
@@ -223,6 +224,7 @@
self._sessionName = (self.setting('SessionName')
or self.defaultConfig()['SessionName'])
self._autoPathSessions = self.setting('UseAutomaticPathSessions')
+ self._alwaysSaveSessions = self.setting('AlwaysSaveSessions')
moduleName = self.setting('SessionModule')
className = moduleName.rsplit('.', 1)[-1]
try:
Modified: Webware/trunk/WebKit/Configs/Application.config
==============================================================================
--- Webware/trunk/WebKit/Configs/Application.config Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/Configs/Application.config Fri Mar 4 02:54:45 2011 (r8156)
@@ -57,6 +57,8 @@
MaxDynamicMemorySessions = 10000
# Time in minutes when to move sessions from memory to disk:
DynamicSessionTimeout = 15
+# Set to False if sessions should be saved only when dirty:
+AlwaysSaveSessions = True
# The session ID can be prefixed with "hostname" or any other string:
SessionPrefix = None # no prefix to session ID
IgnoreInvalidSession = True
Modified: Webware/trunk/WebKit/Docs/Configuration.txt
==============================================================================
--- Webware/trunk/WebKit/Docs/Configuration.txt Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/Docs/Configuration.txt Fri Mar 4 02:54:45 2011 (r8156)
@@ -156,6 +156,15 @@
Determines the amount of time (expressed in minutes) that passes
before a user's session will timeout. When a session times out,
all data associated with that session is lost. Default: ``60``.
+``AlwaysSaveSessions``:
+ If False, then sessions will only be saved if they have been changed.
+ This is more performant and avoids problems with concurrent requests
+ made by the same user if sessions are not shared between these requests,
+ as is the case for session stores other than ``Memory`` or ``Dynamic``.
+ Note that in this case the last access time is not saved either, so
+ sessions may time out if they are not altered. You can call ``setDirty()``
+ on sessions to force saving unaltered sessions in this case. If True,
+ then sessions will always be saved. Default: ``True``.
``IgnoreInvalidSession``:
If False, then an error message will be returned to the user if
the user's session has timed out or doesn't exist. If True, then
Modified: Webware/trunk/WebKit/Session.py
==============================================================================
--- Webware/trunk/WebKit/Session.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/Session.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -49,7 +49,7 @@
def __init__(self, trans, identifier=None):
self._lastAccessTime = self._creationTime = time()
- self._isExpired = False
+ self._isExpired = self._dirty = False
self._numTrans = 0
self._values = {}
app = trans.application()
@@ -106,6 +106,14 @@
"""
return self._identifier
+ def isDirty(self):
+ """Check whether the session is dirty (has unsaved changes)."""
+ return self._dirty
+
+ def setDirty(self, dirty=True):
+ """Set the dirty status of the session."""
+ self._dirty = dirty
+
def isExpired(self):
"""Check whether the session has been previously expired.
@@ -137,6 +145,7 @@
"""
self._lastAccessTime = 0
self._values = {}
+ self._dirty = False
self._timeout = 0
@@ -153,9 +162,11 @@
def setValue(self, name, value):
self._values[name] = value
+ self._dirty = True
def delValue(self, name):
del self._values[name]
+ self._dirty = True
def values(self):
return self._values
@@ -233,7 +244,7 @@
## Exception reports ##
_exceptionReportAttrNames = \
- 'lastAccessTime isExpired numTrans timeout values'.split()
+ 'isDirty isExpired lastAccessTime numTrans timeout values'.split()
def writeExceptionReport(self, handler):
handler.writeTitle(self.__class__.__name__)
Modified: Webware/trunk/WebKit/SessionDynamicStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionDynamicStore.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/SessionDynamicStore.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -96,9 +96,10 @@
finally:
self._lock.release()
- def __setitem__(self, key, item):
+ def __setitem__(self, key, value):
"""Set a sessing item, saving it to the memory store for now."""
- self._memoryStore[key] = item
+ value.setDirty(False)
+ self._memoryStore[key] = value
def __delitem__(self, key):
"""Delete a session item from the memory and the file store."""
@@ -208,19 +209,20 @@
def storeSession(self, session):
"""Save potentially changed session in the store."""
- key = session.identifier()
- self._lock.acquire()
- try:
- if key in self:
- if key in self._memoryStore:
- if self._memoryStore[key] is not session:
- self._memoryStore[key] = session
+ if self._alwaysSave or session.isDirty():
+ key = session.identifier()
+ self._lock.acquire()
+ try:
+ if key in self:
+ if key in self._memoryStore:
+ if self._memoryStore[key] is not session:
+ self._memoryStore[key] = session
+ else:
+ self._fileStore[key] = session
else:
- self._fileStore[key] = session
- else:
- self[key] = session
- finally:
- self._lock.release()
+ self[key] = session
+ finally:
+ self._lock.release()
def storeAllSessions(self):
"""Permanently save all sessions in the store."""
Modified: Webware/trunk/WebKit/SessionFileStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionFileStore.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/SessionFileStore.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -60,7 +60,7 @@
raise KeyError(key) # session file not found
try:
try:
- item = self.decoder()(sessionFile)
+ value = self.decoder()(sessionFile)
finally:
sessionFile.close()
except Exception:
@@ -73,29 +73,35 @@
raise KeyError(key)
finally:
self._lock.release()
- return item
+ return value
def __setitem__(self, key, value):
"""Set a session item, saving it to a session file."""
- filename = self.filenameForKey(key)
- self._lock.acquire()
- try:
+ dirty = value.isDirty()
+ if self._alwaysSave or dirty:
+ filename = self.filenameForKey(key)
+ self._lock.acquire()
try:
- sessionFile = open(filename, 'wb')
+ if dirty:
+ value.setDirty(False)
try:
+ sessionFile = open(filename, 'wb')
try:
- self.encoder()(value, sessionFile)
- finally:
- sessionFile.close()
- except Exception:
- # remove the session file because it is corrupt
- os.remove(filename)
- raise # raise original exception
- except Exception: # error pickling the session
- print "Error saving session to disk:", key
- self.application().handleException()
- finally:
- self._lock.release()
+ try:
+ self.encoder()(value, sessionFile)
+ finally:
+ sessionFile.close()
+ except Exception:
+ # remove the session file because it is corrupt
+ os.remove(filename)
+ raise # raise original exception
+ except Exception: # error pickling the session
+ if dirty:
+ value.setDirty()
+ print "Error saving session to disk:", key
+ self.application().handleException()
+ finally:
+ self._lock.release()
def __delitem__(self, key):
"""Delete a session item, removing its session file."""
@@ -172,8 +178,8 @@
def storeSession(self, session):
"""Save session, writing it to the session file now."""
- key = session.identifier()
- self[key] = session
+ if self._alwaysSave or session.isDirty():
+ self[session.identifier()] = session
def storeAllSessions(self):
"""Permanently save all sessions in the store."""
Modified: Webware/trunk/WebKit/SessionMemcachedStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionMemcachedStore.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/SessionMemcachedStore.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -107,18 +107,24 @@
raise KeyError(key)
return value
- def __setitem__(self, key, item):
+ def __setitem__(self, key, value):
"""Set a session item, writing it to the store."""
if debug:
- print ">> setitem(%s, %s)" % (key, item)
- try:
- if not self._client.set(self.mcKey(key), item,
- time=self._sessionTimeout):
- raise ValueError("Setting value in the memcache failed.")
- except Exception, exc:
- # Not able to store the session is a failure
- print "Error saving session '%s' to memcache: %s" % (key, exc)
- self.application().handleException()
+ print ">> setitem(%s, %s)" % (key, value)
+ dirty = value.isDirty()
+ if self._alwaysSave or dirty:
+ if dirty:
+ value.setDirty(False)
+ try:
+ if not self._client.set(self.mcKey(key), value,
+ time=self._sessionTimeout):
+ raise ValueError("Setting value in the memcache failed.")
+ except Exception, exc:
+ if dirty:
+ value.setDirty()
+ # Not able to store the session is a failure
+ print "Error saving session '%s' to memcache: %s" % (key, exc)
+ self.application().handleException()
def __delitem__(self, key):
"""Delete a session item from the store.
Modified: Webware/trunk/WebKit/SessionMemoryStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionMemoryStore.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/SessionMemoryStore.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -47,6 +47,7 @@
def __setitem__(self, key, value):
"""Set a session item, saving it to the store."""
+ value.setDirty(False)
self._store[key] = value
def __delitem__(self, key):
@@ -90,9 +91,10 @@
def storeSession(self, session):
"""Save already potentially changed session in the store."""
- key = session.identifier()
- if key not in self or self[key] is not session:
- self[key] = session
+ if self._alwaysSave or session.isDirty():
+ key = session.identifier()
+ if key not in self or self[key] is not session:
+ self[key] = session
def storeAllSessions(self):
"""Permanently save all sessions in the store."""
Modified: Webware/trunk/WebKit/SessionShelveStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionShelveStore.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/SessionShelveStore.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -48,11 +48,20 @@
def __setitem__(self, key, value):
"""Set a session item, writing it to the store."""
# concurrent write access is not supported
- self._lock.acquire()
- try:
- self._store[key] = value
- finally:
- self._lock.release()
+ dirty = value.isDirty()
+ if self._alwaysSave or dirty:
+ self._lock.acquire()
+ try:
+ if dirty:
+ value.setDirty(False)
+ try:
+ self._store[key] = value
+ except Exception:
+ if dirty:
+ value.setDirty()
+ raise # raise original exception
+ finally:
+ self._lock.release()
def __delitem__(self, key):
"""Delete a session item from the store."""
Modified: Webware/trunk/WebKit/SessionStore.py
==============================================================================
--- Webware/trunk/WebKit/SessionStore.py Mon Feb 21 14:45:19 2011 (r8155)
+++ Webware/trunk/WebKit/SessionStore.py Fri Mar 4 02:54:45 2011 (r8156)
@@ -61,7 +61,7 @@
"""
self._app = app
-
+ self._alwaysSave = app._alwaysSaveSessions
self._encoder = dumpWithHighestProtocol
self._decoder = load
|