Rewrote Session_storage class, because it was periodically raising ProgrammingErrors due to simultaneous cursor access from different threads.
This commit is contained in:
parent
3a5ca1d462
commit
22c49a9590
|
@ -1,29 +1,79 @@
|
|||
import cherrypy
|
||||
from psycopg2 import ProgrammingError
|
||||
from cherrypy.filters.sessionfilter import PostgreSQLStorage
|
||||
import cPickle as pickle
|
||||
from psycopg2 import ProgrammingError
|
||||
|
||||
|
||||
class Session_storage( PostgreSQLStorage ):
|
||||
"""
|
||||
A wrapper for CherryPy's PostgreSQLStorage class that commits the current transaction to the
|
||||
database so session changes actually take effect.
|
||||
class Session_storage( object ):
|
||||
"""
|
||||
A CherryPy session storage class, originally based on CherryPy's PostgreSQLStorage. It assumes
|
||||
a table like this:
|
||||
|
||||
create table session (
|
||||
id text,
|
||||
data text,
|
||||
expiration_time timestamp
|
||||
)
|
||||
|
||||
It differs from PostgreSQLStorage in the following ways:
|
||||
|
||||
* changes to the database are actually committed after they are made
|
||||
* a new cursor is created for each database access to prevent problems with multiple threads
|
||||
* a connection is requested from cherrypy.root.database instead of a session_filter.get_db method
|
||||
* __del__ is not implemented because it should not be relied upon
|
||||
* no locking is implemented
|
||||
"""
|
||||
def __init__( self ):
|
||||
self.db = cherrypy.root.database.get_connection()
|
||||
self.cursor = self.db.cursor()
|
||||
self.conn = cherrypy.root.database.get_connection()
|
||||
|
||||
def load( self, id ):
|
||||
cursor = self.conn.cursor()
|
||||
|
||||
def load( self, *args, **kwargs ):
|
||||
try:
|
||||
return PostgreSQLStorage.load( self, *args, **kwargs )
|
||||
# catch "ProgrammingError: no results to fetch" from self.cursor.fetchall()
|
||||
except ProgrammingError:
|
||||
# Select session data from table
|
||||
cursor.execute(
|
||||
'select data, expiration_time from session where id=%s',
|
||||
(id,))
|
||||
rows = cursor.fetchall()
|
||||
if not rows:
|
||||
return None
|
||||
pickled_data, expiration_time = rows[0]
|
||||
# Unpickle data
|
||||
data = pickle.loads(pickled_data)
|
||||
return (data, expiration_time)
|
||||
|
||||
def save( self, id, data, expiration_time ):
|
||||
cursor = self.conn.cursor()
|
||||
|
||||
def save( self, *args, **kwargs ):
|
||||
PostgreSQLStorage.save( self, *args, **kwargs )
|
||||
self.db.commit()
|
||||
# Try to delete session if it was already there
|
||||
cursor.execute(
|
||||
'delete from session where id=%s',
|
||||
(id,))
|
||||
# Pickle data
|
||||
pickled_data = pickle.dumps(data)
|
||||
# Insert new session data
|
||||
cursor.execute(
|
||||
'insert into session (id, data, expiration_time) values (%s, %s, %s)',
|
||||
(id, pickled_data, expiration_time))
|
||||
|
||||
def clean_up( self, *args, **kwargs ):
|
||||
PostgreSQLStorage.clean_up( self, *args, **kwargs )
|
||||
self.db.commit()
|
||||
self.conn.commit()
|
||||
|
||||
def clean_up( self, sess ):
|
||||
cursor = self.conn.cursor()
|
||||
|
||||
now = datetime.datetime.now()
|
||||
cursor.execute(
|
||||
'select data from session where expiration_time < %s',
|
||||
(now,))
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
sess.on_delete_session(row[0])
|
||||
cursor.execute(
|
||||
'delete from session where expiration_time < %s',
|
||||
(now,))
|
||||
|
||||
self.conn.commit()
|
||||
|
||||
def acquire_lock( self ):
|
||||
raise NotImplemented()
|
||||
|
||||
def release_lock( self ):
|
||||
raise NotImplemented()
|
||||
|
|
Reference in New Issue