witten
/
luminotes
Archived
1
0
Fork 0

Fixed database transaction leak by wrapping every exposed database-using

controller method with a new @end_transaction() decorator. This
decorator is responsible for rolling back unfinished transactions.
This commit is contained in:
Dan Helfman 2008-03-17 21:17:00 +00:00
parent a4387ea371
commit f3b0d563c1
7 changed files with 69 additions and 4 deletions

1
NEWS
View File

@ -1,5 +1,6 @@
1.2.14: March ??, 2008 1.2.14: March ??, 2008
* Added ability to reorder notebooks on the right side of the page. * Added ability to reorder notebooks on the right side of the page.
* Fixed database transaction leak that was wasting memory.
1.2.13: March 11, 2008 1.2.13: March 11, 2008
* When the "all notes" note is the only note open, it now actually hides when * When the "all notes" note is the only note open, it now actually hides when

View File

@ -1,6 +1,7 @@
import re import re
import os import os
import sha import sha
import cherrypy
import psycopg2 as psycopg import psycopg2 as psycopg
from psycopg2.pool import PersistentConnectionPool from psycopg2.pool import PersistentConnectionPool
import random import random
@ -320,6 +321,20 @@ class Database( object ):
self.__pool.closeall() self.__pool.closeall()
def end_transaction( function ):
"""
Decorator that prevents transaction leaks by rolling back any transactions left open when the
wrapped function returns or raises.
"""
def rollback( *args, **kwargs ):
try:
return function( *args, **kwargs )
finally:
cherrypy.root.database.rollback()
return rollback
class Valid_id( object ): class Valid_id( object ):
""" """
Validator for an object id. Validator for an object id.

View File

@ -7,7 +7,7 @@ import cherrypy
from threading import Lock, Event from threading import Lock, Event
from Expose import expose from Expose import expose
from Validate import validate, Valid_int, Validation_error from Validate import validate, Valid_int, Validation_error
from Database import Valid_id from Database import Valid_id, end_transaction
from Users import grab_user_id from Users import grab_user_id
from Expire import strongly_expire from Expire import strongly_expire
from model.File import File from model.File import File
@ -232,6 +232,7 @@ class Files( object ):
self.__users = users self.__users = users
@expose() @expose()
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
file_id = Valid_id(), file_id = Valid_id(),
@ -280,6 +281,7 @@ class Files( object ):
@expose( view = Upload_page ) @expose( view = Upload_page )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -314,6 +316,7 @@ class Files( object ):
@expose( view = Blank_page ) @expose( view = Blank_page )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
upload = (), upload = (),
@ -383,6 +386,7 @@ class Files( object ):
@expose() @expose()
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
file_id = Valid_id(), file_id = Valid_id(),
@ -449,6 +453,7 @@ class Files( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
file_id = Valid_id(), file_id = Valid_id(),
@ -487,6 +492,7 @@ class Files( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
file_id = Valid_id(), file_id = Valid_id(),
@ -523,6 +529,7 @@ class Files( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
file_id = Valid_id(), file_id = Valid_id(),

View File

@ -3,7 +3,7 @@ import cherrypy
from datetime import datetime from datetime import datetime
from Expose import expose from Expose import expose
from Validate import validate, Valid_string, Validation_error, Valid_bool from Validate import validate, Valid_string, Validation_error, Valid_bool
from Database import Valid_id, Valid_revision from Database import Valid_id, Valid_revision, end_transaction
from Users import grab_user_id from Users import grab_user_id
from Expire import strongly_expire from Expire import strongly_expire
from Html_nuker import Html_nuker from Html_nuker import Html_nuker
@ -55,6 +55,7 @@ class Notebooks( object ):
@expose( view = Main_page ) @expose( view = Main_page )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -207,6 +208,7 @@ class Notebooks( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -267,6 +269,7 @@ class Notebooks( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -358,6 +361,7 @@ class Notebooks( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -395,6 +399,7 @@ class Notebooks( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -445,6 +450,7 @@ class Notebooks( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -561,6 +567,7 @@ class Notebooks( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -614,6 +621,7 @@ class Notebooks( object ):
return dict( storage_bytes = 0 ) return dict( storage_bytes = 0 )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -670,6 +678,7 @@ class Notebooks( object ):
return dict( storage_bytes = 0 ) return dict( storage_bytes = 0 )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -722,6 +731,7 @@ class Notebooks( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -770,6 +780,7 @@ class Notebooks( object ):
@expose( view = Json ) @expose( view = Json )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -804,6 +815,7 @@ class Notebooks( object ):
@expose( view = Html_file ) @expose( view = Html_file )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -839,6 +851,7 @@ class Notebooks( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
@ -885,6 +898,7 @@ class Notebooks( object ):
return notebook return notebook
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -936,6 +950,7 @@ class Notebooks( object ):
return dict() return dict()
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -991,6 +1006,7 @@ class Notebooks( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -1033,6 +1049,7 @@ class Notebooks( object ):
return dict( storage_bytes = user.storage_bytes ) return dict( storage_bytes = user.storage_bytes )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -1073,6 +1090,7 @@ class Notebooks( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -1144,6 +1162,7 @@ class Notebooks( object ):
return dict() return dict()
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),

View File

@ -6,7 +6,7 @@ from Validate import validate, Valid_int, Valid_string
from Notebooks import Notebooks from Notebooks import Notebooks
from Users import Users, grab_user_id from Users import Users, grab_user_id
from Files import Files from Files import Files
from Database import Valid_id from Database import Valid_id, end_transaction
from model.Note import Note from model.Note import Note
from model.Notebook import Notebook from model.Notebook import Notebook
from model.User import User from model.User import User
@ -50,6 +50,7 @@ class Root( object ):
self.__suppress_exceptions = suppress_exceptions # used for unit tests self.__suppress_exceptions = suppress_exceptions # used for unit tests
@expose( Main_page ) @expose( Main_page )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
note_title = unicode, note_title = unicode,
@ -136,6 +137,7 @@ class Root( object ):
@expose( view = Front_page ) @expose( view = Front_page )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
@ -172,6 +174,7 @@ class Root( object ):
return result return result
@expose( view = Tour_page ) @expose( view = Tour_page )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
@ -191,6 +194,7 @@ class Root( object ):
return dict( redirect = u"/tour" ) return dict( redirect = u"/tour" )
@expose( view = Main_page, rss = Notebook_rss ) @expose( view = Main_page, rss = Notebook_rss )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
start = Valid_int( min = 0 ), start = Valid_int( min = 0 ),
@ -225,6 +229,7 @@ class Root( object ):
return result return result
@expose( view = Main_page ) @expose( view = Main_page )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
@ -245,6 +250,7 @@ class Root( object ):
return result return result
@expose( view = Main_page ) @expose( view = Main_page )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
@ -266,6 +272,7 @@ class Root( object ):
@expose( view = Main_page ) @expose( view = Main_page )
@strongly_expire @strongly_expire
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
@ -307,6 +314,7 @@ class Root( object ):
# TODO: move this method to controller.Notebooks, and maybe give it a more sensible name # TODO: move this method to controller.Notebooks, and maybe give it a more sensible name
@expose( view = Json ) @expose( view = Json )
@end_transaction
def next_id( self ): def next_id( self ):
""" """
Return the next available database object id for a new note. This id is guaranteed to be unique Return the next available database object id for a new note. This id is guaranteed to be unique

View File

@ -11,7 +11,7 @@ from model.Password_reset import Password_reset
from model.Invite import Invite from model.Invite import Invite
from Expose import expose from Expose import expose
from Validate import validate, Valid_string, Valid_bool, Validation_error from Validate import validate, Valid_string, Valid_bool, Validation_error
from Database import Valid_id from Database import Valid_id, end_transaction
from Expire import strongly_expire from Expire import strongly_expire
from view.Json import Json from view.Json import Json
from view.Main_page import Main_page from view.Main_page import Main_page
@ -196,6 +196,7 @@ class Users( object ):
self.__rate_plans = rate_plans self.__rate_plans = rate_plans
@expose( view = Json ) @expose( view = Json )
@end_transaction
@update_auth @update_auth
@validate( @validate(
username = ( Valid_string( min = 1, max = 30 ), valid_username ), username = ( Valid_string( min = 1, max = 30 ), valid_username ),
@ -284,6 +285,7 @@ class Users( object ):
) )
@expose() @expose()
@end_transaction
@grab_user_id @grab_user_id
@update_auth @update_auth
def demo( self, user_id = None ): def demo( self, user_id = None ):
@ -347,6 +349,7 @@ class Users( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@update_auth @update_auth
@validate( @validate(
username = ( Valid_string( min = 1, max = 30 ), valid_username ), username = ( Valid_string( min = 1, max = 30 ), valid_username ),
@ -403,6 +406,7 @@ class Users( object ):
) )
@expose() @expose()
@end_transaction
@update_auth @update_auth
def logout( self ): def logout( self ):
""" """
@ -528,6 +532,7 @@ class Users( object ):
return False return False
@expose( view = Json ) @expose( view = Json )
@end_transaction
@validate( @validate(
email_address = ( Valid_string( min = 1, max = 60 ), valid_email_address ), email_address = ( Valid_string( min = 1, max = 60 ), valid_email_address ),
send_reset_button = unicode, send_reset_button = unicode,
@ -585,6 +590,7 @@ class Users( object ):
@expose( view = Main_page ) @expose( view = Main_page )
@strongly_expire @strongly_expire
@end_transaction
@validate( @validate(
password_reset_id = Valid_id(), password_reset_id = Valid_id(),
) )
@ -636,6 +642,7 @@ class Users( object ):
return result return result
@expose( view = Json ) @expose( view = Json )
@end_transaction
def reset_password( self, password_reset_id, reset_button, **new_passwords ): def reset_password( self, password_reset_id, reset_button, **new_passwords ):
""" """
Reset all the users with the provided passwords. Reset all the users with the provided passwords.
@ -705,6 +712,7 @@ class Users( object ):
return dict( redirect = u"/" ) return dict( redirect = u"/" )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -836,6 +844,7 @@ class Users( object ):
) )
@expose( view = Json ) @expose( view = Json )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
notebook_id = Valid_id(), notebook_id = Valid_id(),
@ -880,6 +889,7 @@ class Users( object ):
) )
@expose( view = Main_page ) @expose( view = Main_page )
@end_transaction
@grab_user_id @grab_user_id
@validate( @validate(
invite_id = Valid_id(), invite_id = Valid_id(),
@ -979,6 +989,7 @@ class Users( object ):
self.__database.commit() self.__database.commit()
@expose( view = Blank_page ) @expose( view = Blank_page )
@end_transaction
def paypal_notify( self, **params ): def paypal_notify( self, **params ):
""" """
Notify Luminotes of payments, subscriptions, cancellations, refunds, etc. Notify Luminotes of payments, subscriptions, cancellations, refunds, etc.
@ -1082,6 +1093,7 @@ class Users( object ):
return dict() return dict()
@expose( view = Main_page ) @expose( view = Main_page )
@end_transaction
@grab_user_id @grab_user_id
def thanks( self, **params ): def thanks( self, **params ):
""" """

View File

@ -77,5 +77,8 @@ class Stub_database( object ):
def commit( self ): def commit( self ):
pass pass
def rollback( self ):
pass
def close( self ): def close( self ):
pass pass