Ticket #9964: 9964-against-10052.patch
File 9964-against-10052.patch, 27.7 KB (added by , 16 years ago) |
---|
-
django/db/transaction/fast_select.py
10 10 11 11 Managed transactions don't do those commits, but will need some kind of manual 12 12 or implicit commits or rollbacks. 13 14 This is a version of the transaction manager which marks transactions 15 as 'clean' or 'dirty', and neglects to close 'clean' transactions to 16 improve performnce. 17 18 TODO: Explain when this breaks, and when it is less efficient 13 19 """ 14 20 21 15 22 try: 16 23 import thread 17 24 except ImportError: 18 25 import dummy_thread as thread 19 try: 20 from functools import wraps 21 except ImportError: 22 from django.utils.functional import wraps # Python 2.3, 2.4 fallback. 26 23 27 from django.db import connection 24 28 from django.conf import settings 25 29 26 class TransactionManagementError(Exception): 27 """ 28 This exception is thrown when something bad happens with transaction 29 management. 30 """ 31 pass 30 from django.db.transaction import TransactionManagementError 31 from django.db.transaction import state, savepoint_state 32 from django.db.transaction import is_managed, clean_savepoints, rollback() 32 33 33 # The states are dictionaries of lists. The key to the dict is the current34 # thread and the list is handled as a stack of values.35 state = {}36 savepoint_state = {}37 38 34 # The dirty flag is set by *_unless_managed functions to denote that the 39 35 # code under transaction management has changed things to require a 40 36 # database commit. … … 109 105 raise TransactionManagementError("This code isn't under transaction management") 110 106 clean_savepoints() 111 107 112 def clean_savepoints():113 thread_ident = thread.get_ident()114 if thread_ident in savepoint_state:115 del savepoint_state[thread_ident]116 117 def is_managed():118 """119 Checks whether the transaction manager is in manual or in auto state.120 """121 thread_ident = thread.get_ident()122 if thread_ident in state:123 if state[thread_ident]:124 return state[thread_ident][-1]125 return settings.TRANSACTIONS_MANAGED126 127 def managed(flag=True):128 """129 Puts the transaction manager into a manual state: managed transactions have130 to be committed explicitly by the user. If you switch off transaction131 management and there is a pending commit/rollback, the data will be132 commited.133 """134 thread_ident = thread.get_ident()135 top = state.get(thread_ident, None)136 if top:137 top[-1] = flag138 if not flag and is_dirty():139 connection._commit()140 set_clean()141 else:142 raise TransactionManagementError("This code isn't under transaction management")143 144 def commit_unless_managed():145 """146 Commits changes if the system is not in managed transaction mode.147 """148 if not is_managed():149 connection._commit()150 clean_savepoints()151 else:152 set_dirty()153 154 def rollback_unless_managed():155 """156 Rolls back changes if the system is not in managed transaction mode.157 """158 if not is_managed():159 connection._rollback()160 else:161 set_dirty()162 163 def commit():164 """165 Does the commit itself and resets the dirty flag.166 """167 connection._commit()168 set_clean()169 170 def rollback():171 """172 This function does the rollback itself and resets the dirty flag.173 """174 connection._rollback()175 set_clean()176 177 def savepoint():178 """179 Creates a savepoint (if supported and required by the backend) inside the180 current transaction. Returns an identifier for the savepoint that will be181 used for the subsequent rollback or commit.182 """183 thread_ident = thread.get_ident()184 if thread_ident in savepoint_state:185 savepoint_state[thread_ident].append(None)186 else:187 savepoint_state[thread_ident] = [None]188 tid = str(thread_ident).replace('-', '')189 sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))190 connection._savepoint(sid)191 return sid192 193 def savepoint_rollback(sid):194 """195 Rolls back the most recent savepoint (if one exists). Does nothing if196 savepoints are not supported.197 """198 if thread.get_ident() in savepoint_state:199 connection._savepoint_rollback(sid)200 201 def savepoint_commit(sid):202 """203 Commits the most recent savepoint (if one exists). Does nothing if204 savepoints are not supported.205 """206 if thread.get_ident() in savepoint_state:207 connection._savepoint_commit(sid)208 209 ##############210 # DECORATORS #211 ##############212 213 def autocommit(func):214 """215 Decorator that activates commit on save. This is Django's default behavior;216 this decorator is useful if you globally activated transaction management in217 your settings file and want the default behavior in some view functions.218 """219 def _autocommit(*args, **kw):220 try:221 enter_transaction_management(managed=False)222 managed(False)223 return func(*args, **kw)224 finally:225 leave_transaction_management()226 return wraps(func)(_autocommit)227 228 def commit_on_success(func):229 """230 This decorator activates commit on response. This way, if the view function231 runs successfully, a commit is made; if the viewfunc produces an exception,232 a rollback is made. This is one of the most common ways to do transaction233 control in web apps.234 """235 def _commit_on_success(*args, **kw):236 try:237 enter_transaction_management()238 managed(True)239 try:240 res = func(*args, **kw)241 except:242 # All exceptions must be handled here (even string ones).243 if is_dirty():244 rollback()245 raise246 else:247 if is_dirty():248 commit()249 return res250 finally:251 leave_transaction_management()252 return wraps(func)(_commit_on_success)253 254 def commit_manually(func):255 """256 Decorator that activates manual transaction control. It just disables257 automatic transaction control and doesn't do any commit/rollback of its258 own -- it's up to the user to call the commit and rollback functions259 themselves.260 """261 def _commit_manually(*args, **kw):262 try:263 enter_transaction_management()264 managed(True)265 return func(*args, **kw)266 finally:267 leave_transaction_management()268 269 return wraps(func)(_commit_manually) -
django/db/transaction/safe.py
1 """ 2 This module implements a transaction manager that can be used to define 3 transaction handling in a request or view function. It is used by transaction 4 control middleware and decorators. 5 6 The transaction manager can be in managed or in auto state. Auto state means the 7 system is using a commit-on-save strategy (actually it's more like 8 commit-on-change). As soon as the .save() or .delete() (or related) methods are 9 called, a commit is made. 10 11 Managed transactions don't do those commits, but will need some kind of manual 12 or implicit commits or rollbacks. 13 14 This is a version of the transaction manager which treats all transactions 15 as 'dirty', so that they are always closed. 16 """ 17 18 try: 19 import thread 20 except ImportError: 21 import dummy_thread as thread 22 try: 23 from functools import wraps 24 except ImportError: 25 from django.utils.functional import wraps # Python 2.3, 2.4 fallback. 26 from django.db import connection 27 from django.conf import settings 28 29 from django.db.transaction import TransactionManagementError 30 from django.db.transaction import state, savepoint_state 31 from django.db.transaction import is_managed, clean_savepoints, rollback 32 33 34 def enter_transaction_management(managed=True): 35 """ 36 Enters transaction management for a running thread. It must be balanced with 37 the appropriate leave_transaction_management call, since the actual state is 38 managed as a stack. 39 40 The state and dirty flag are carried over from the surrounding block or 41 from the settings, if there is no surrounding block (dirty is always false 42 when no current block is running). 43 """ 44 thread_ident = thread.get_ident() 45 if thread_ident in state and state[thread_ident]: 46 state[thread_ident].append(state[thread_ident][-1]) 47 else: 48 state[thread_ident] = [] 49 state[thread_ident].append(settings.TRANSACTIONS_MANAGED) 50 connection._enter_transaction_management(managed) 51 52 def leave_transaction_management(): 53 """ 54 Leaves transaction management for a running thread. 55 """ 56 connection._leave_transaction_management(is_managed()) 57 thread_ident = thread.get_ident() 58 if thread_ident in state and state[thread_ident]: 59 del state[thread_ident][-1] 60 else: 61 raise TransactionManagementError("This code isn't under transaction management") 62 # Anything not committed should be rolled back 63 # TODO: spurious database hits here? Just leave things be? 64 rollback() 65 66 def is_dirty(): 67 """ 68 All transactions are always dirty 69 """ 70 return True 71 72 def set_dirty(): 73 """ 74 All transactions are always dirty 75 """ 76 pass 77 78 def set_clean(): 79 """ 80 All transactions are always dirty 81 """ 82 # TODO: Really? Retained from the fast_select version 83 clean_savepoints() 84 85 86 87 -
django/db/transaction/__init__.py
1 """ 2 This module supports selection, by a setting, of a 3 transaction management "policy". Current policies 4 are "fast-select" (which leaves clean transactions 5 open) and "safe" (which makes sure all transactions 6 are closed, whether they are committed or rolled back. 7 8 TODO: Document when fast is not safe, and when fast is 9 not even fast (repatable-read w/out MVCC). 10 """ 11 12 ############### 13 # Common base # 14 ############### 15 16 # These are common to the policies 17 try: 18 import thread 19 except ImportError: 20 import dummy_thread as thread 21 try: 22 from functools import wraps 23 except ImportError: 24 from django.utils.functional import wraps # Python 2.3, 2.4 fallback. 25 from django.db import connection 26 from django.conf import settings 27 from django.core.exceptions import ImproperlyConfigured 28 29 import fast_select 30 import safe 31 32 class TransactionManagementError(Exception): 33 """ 34 This exception is thrown when something bad happens with transaction 35 management. 36 """ 37 pass 38 39 # The states are dictionaries of lists. The key to the dict is the current 40 # thread and the list is handled as a stack of values. 41 state = {} 42 savepoint_state = {} 43 44 def is_managed(): 45 """ 46 Checks whether the transaction manager is in manual or in auto state. 47 """ 48 thread_ident = thread.get_ident() 49 if thread_ident in state: 50 if state[thread_ident]: 51 return state[thread_ident][-1] 52 return settings.TRANSACTIONS_MANAGED 53 54 def managed(flag=True): 55 """ 56 Puts the transaction manager into a manual state: managed transactions have 57 to be committed explicitly by the user. If you switch off transaction 58 management and there is a pending commit/rollback, the data will be 59 commited. 60 """ 61 thread_ident = thread.get_ident() 62 top = state.get(thread_ident, None) 63 if top: 64 top[-1] = flag 65 if not flag and is_dirty(): 66 connection._commit() 67 set_clean() 68 else: 69 raise TransactionManagementError("This code isn't under transaction management") 70 71 def commit_unless_managed(): 72 """ 73 Commits changes if the system is not in managed transaction mode. 74 """ 75 if not is_managed(): 76 connection._commit() 77 clean_savepoints() 78 else: 79 set_dirty() 80 81 def rollback_unless_managed(): 82 """ 83 Rolls back changes if the system is not in managed transaction mode. 84 """ 85 if not is_managed(): 86 connection._rollback() 87 else: 88 set_dirty() 89 90 def commit(): 91 """ 92 Does the commit itself and resets the dirty flag. 93 """ 94 connection._commit() 95 set_clean() 96 97 def rollback(): 98 """ 99 This function does the rollback itself and resets the dirty flag. 100 """ 101 connection._rollback() 102 set_clean() 103 104 def clean_savepoints(): 105 thread_ident = thread.get_ident() 106 if thread_ident in savepoint_state: 107 del savepoint_state[thread_ident] 108 109 def savepoint(): 110 """ 111 Creates a savepoint (if supported and required by the backend) inside the 112 current transaction. Returns an identifier for the savepoint that will be 113 used for the subsequent rollback or commit. 114 """ 115 thread_ident = thread.get_ident() 116 if thread_ident in savepoint_state: 117 savepoint_state[thread_ident].append(None) 118 else: 119 savepoint_state[thread_ident] = [None] 120 tid = str(thread_ident).replace('-', '') 121 sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident])) 122 connection._savepoint(sid) 123 return sid 124 125 def savepoint_rollback(sid): 126 """ 127 Rolls back the most recent savepoint (if one exists). Does nothing if 128 savepoints are not supported. 129 """ 130 if thread.get_ident() in savepoint_state: 131 connection._savepoint_rollback(sid) 132 133 def savepoint_commit(sid): 134 """ 135 Commits the most recent savepoint (if one exists). Does nothing if 136 savepoints are not supported. 137 """ 138 if thread.get_ident() in savepoint_state: 139 connection._savepoint_commit(sid) 140 141 ##################### 142 # Policy controlled # 143 ##################### 144 145 # Implementation note: we use a simple strategy of wrapping 146 # self-rewriting functions. Cost is an extra method call, 147 # which should be acceptable. 148 # 149 # Self-rewriting funcs: consider 150 # 151 # def f(): 152 # global f 153 # f = some_other_func 154 # return f() 155 # 156 # After the first call, any call to f goes directly to some_other_func -- 157 # provided only that the name f is accessed via the module's global namespace. 158 # To ensure this, we add a slight variation: we use a self-rewriting _f, 159 # and f just calls _f (which it gets from module scope). 160 # 161 #TODO: This pattern can be factored out in several ways. 162 163 def _get_policy_module(): 164 policy = getattr(settings, 'DATABASE_TRANSACTION_POLICY', 'fast_select') 165 if policy=='fast_select': 166 return fast_select 167 elif policy=='safe': 168 return safe 169 else: 170 raise ImproperlyConfigured, "Unknown transaction policy '%s'" % str(policy) 171 172 def enter_transaction_management(managed=True): 173 return _enter_transaction_management(managed) 174 def _enter_transaction_management(managed=True): 175 global _enter_transaction_management 176 _enter_transaction_management = _get_policy_module().enter_transaction_management 177 return _enter_transaction_management(managed) 178 179 def leave_transaction_management(): 180 return _leave_transaction_management() 181 def _leave_transaction_management(): 182 global _leave_transaction_management 183 _leave_transaction_management = _get_policy_module().leave_transaction_management 184 return _leave_transaction_management() 185 186 def is_dirty(): 187 return _is_dirty() 188 def _is_dirty(): 189 global _is_dirty 190 _is_dirty = _get_policy_module().is_dirty 191 return _is_dirty() 192 193 def set_dirty(): 194 return _set_dirty() 195 def _set_dirty(): 196 global _set_dirty 197 _set_dirty = _get_policy_module().set_dirty 198 return _set_dirty() 199 200 def set_clean(): 201 return _set_clean() 202 def _set_clean(): 203 global _set_clean 204 _set_clean = _get_policy_module().set_clean 205 return _set_clean() 206 207 ############## 208 # DECORATORS # 209 ############## 210 211 # Decorators use the above functions, and their own code 212 # doesn't need to change to reflect policy; they are 213 # defined here to make sure they reference the right versions 214 # of the functions they use 215 216 def autocommit(func): 217 """ 218 Decorator that activates commit on save. This is Django's default behavior; 219 this decorator is useful if you globally activated transaction management in 220 your settings file and want the default behavior in some view functions. 221 """ 222 def _autocommit(*args, **kw): 223 try: 224 enter_transaction_management(managed=False) 225 managed(False) 226 return func(*args, **kw) 227 finally: 228 leave_transaction_management() 229 return wraps(func)(_autocommit) 230 231 def commit_on_success(func): 232 """ 233 This decorator activates commit on response. This way, if the view function 234 runs successfully, a commit is made; if the viewfunc produces an exception, 235 a rollback is made. This is one of the most common ways to do transaction 236 control in web apps. 237 """ 238 def _commit_on_success(*args, **kw): 239 try: 240 enter_transaction_management() 241 managed(True) 242 try: 243 res = func(*args, **kw) 244 except: 245 # All exceptions must be handled here (even string ones). 246 if is_dirty(): 247 rollback() 248 raise 249 else: 250 if is_dirty(): 251 commit() 252 return res 253 finally: 254 leave_transaction_management() 255 return wraps(func)(_commit_on_success) 256 257 def commit_manually(func): 258 """ 259 Decorator that activates manual transaction control. It just disables 260 automatic transaction control and doesn't do any commit/rollback of its 261 own -- it's up to the user to call the commit and rollback functions 262 themselves. 263 """ 264 def _commit_manually(*args, **kw): 265 try: 266 enter_transaction_management() 267 managed(True) 268 return func(*args, **kw) 269 finally: 270 leave_transaction_management() 271 272 return wraps(func)(_commit_manually) -
django/db/transaction.py
1 """2 This module implements a transaction manager that can be used to define3 transaction handling in a request or view function. It is used by transaction4 control middleware and decorators.5 6 The transaction manager can be in managed or in auto state. Auto state means the7 system is using a commit-on-save strategy (actually it's more like8 commit-on-change). As soon as the .save() or .delete() (or related) methods are9 called, a commit is made.10 11 Managed transactions don't do those commits, but will need some kind of manual12 or implicit commits or rollbacks.13 """14 15 try:16 import thread17 except ImportError:18 import dummy_thread as thread19 try:20 from functools import wraps21 except ImportError:22 from django.utils.functional import wraps # Python 2.3, 2.4 fallback.23 from django.db import connection24 from django.conf import settings25 26 class TransactionManagementError(Exception):27 """28 This exception is thrown when something bad happens with transaction29 management.30 """31 pass32 33 # The states are dictionaries of lists. The key to the dict is the current34 # thread and the list is handled as a stack of values.35 state = {}36 savepoint_state = {}37 38 # The dirty flag is set by *_unless_managed functions to denote that the39 # code under transaction management has changed things to require a40 # database commit.41 dirty = {}42 43 def enter_transaction_management(managed=True):44 """45 Enters transaction management for a running thread. It must be balanced with46 the appropriate leave_transaction_management call, since the actual state is47 managed as a stack.48 49 The state and dirty flag are carried over from the surrounding block or50 from the settings, if there is no surrounding block (dirty is always false51 when no current block is running).52 """53 thread_ident = thread.get_ident()54 if thread_ident in state and state[thread_ident]:55 state[thread_ident].append(state[thread_ident][-1])56 else:57 state[thread_ident] = []58 state[thread_ident].append(settings.TRANSACTIONS_MANAGED)59 if thread_ident not in dirty:60 dirty[thread_ident] = False61 connection._enter_transaction_management(managed)62 63 def leave_transaction_management():64 """65 Leaves transaction management for a running thread. A dirty flag is carried66 over to the surrounding block, as a commit will commit all changes, even67 those from outside. (Commits are on connection level.)68 """69 connection._leave_transaction_management(is_managed())70 thread_ident = thread.get_ident()71 if thread_ident in state and state[thread_ident]:72 del state[thread_ident][-1]73 else:74 raise TransactionManagementError("This code isn't under transaction management")75 if dirty.get(thread_ident, False):76 rollback()77 raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK")78 dirty[thread_ident] = False79 80 def is_dirty():81 """82 Returns True if the current transaction requires a commit for changes to83 happen.84 """85 return dirty.get(thread.get_ident(), False)86 87 def set_dirty():88 """89 Sets a dirty flag for the current thread and code streak. This can be used90 to decide in a managed block of code to decide whether there are open91 changes waiting for commit.92 """93 thread_ident = thread.get_ident()94 if thread_ident in dirty:95 dirty[thread_ident] = True96 else:97 raise TransactionManagementError("This code isn't under transaction management")98 99 def set_clean():100 """101 Resets a dirty flag for the current thread and code streak. This can be used102 to decide in a managed block of code to decide whether a commit or rollback103 should happen.104 """105 thread_ident = thread.get_ident()106 if thread_ident in dirty:107 dirty[thread_ident] = False108 else:109 raise TransactionManagementError("This code isn't under transaction management")110 clean_savepoints()111 112 def clean_savepoints():113 thread_ident = thread.get_ident()114 if thread_ident in savepoint_state:115 del savepoint_state[thread_ident]116 117 def is_managed():118 """119 Checks whether the transaction manager is in manual or in auto state.120 """121 thread_ident = thread.get_ident()122 if thread_ident in state:123 if state[thread_ident]:124 return state[thread_ident][-1]125 return settings.TRANSACTIONS_MANAGED126 127 def managed(flag=True):128 """129 Puts the transaction manager into a manual state: managed transactions have130 to be committed explicitly by the user. If you switch off transaction131 management and there is a pending commit/rollback, the data will be132 commited.133 """134 thread_ident = thread.get_ident()135 top = state.get(thread_ident, None)136 if top:137 top[-1] = flag138 if not flag and is_dirty():139 connection._commit()140 set_clean()141 else:142 raise TransactionManagementError("This code isn't under transaction management")143 144 def commit_unless_managed():145 """146 Commits changes if the system is not in managed transaction mode.147 """148 if not is_managed():149 connection._commit()150 clean_savepoints()151 else:152 set_dirty()153 154 def rollback_unless_managed():155 """156 Rolls back changes if the system is not in managed transaction mode.157 """158 if not is_managed():159 connection._rollback()160 else:161 set_dirty()162 163 def commit():164 """165 Does the commit itself and resets the dirty flag.166 """167 connection._commit()168 set_clean()169 170 def rollback():171 """172 This function does the rollback itself and resets the dirty flag.173 """174 connection._rollback()175 set_clean()176 177 def savepoint():178 """179 Creates a savepoint (if supported and required by the backend) inside the180 current transaction. Returns an identifier for the savepoint that will be181 used for the subsequent rollback or commit.182 """183 thread_ident = thread.get_ident()184 if thread_ident in savepoint_state:185 savepoint_state[thread_ident].append(None)186 else:187 savepoint_state[thread_ident] = [None]188 tid = str(thread_ident).replace('-', '')189 sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))190 connection._savepoint(sid)191 return sid192 193 def savepoint_rollback(sid):194 """195 Rolls back the most recent savepoint (if one exists). Does nothing if196 savepoints are not supported.197 """198 if thread.get_ident() in savepoint_state:199 connection._savepoint_rollback(sid)200 201 def savepoint_commit(sid):202 """203 Commits the most recent savepoint (if one exists). Does nothing if204 savepoints are not supported.205 """206 if thread.get_ident() in savepoint_state:207 connection._savepoint_commit(sid)208 209 ##############210 # DECORATORS #211 ##############212 213 def autocommit(func):214 """215 Decorator that activates commit on save. This is Django's default behavior;216 this decorator is useful if you globally activated transaction management in217 your settings file and want the default behavior in some view functions.218 """219 def _autocommit(*args, **kw):220 try:221 enter_transaction_management(managed=False)222 managed(False)223 return func(*args, **kw)224 finally:225 leave_transaction_management()226 return wraps(func)(_autocommit)227 228 def commit_on_success(func):229 """230 This decorator activates commit on response. This way, if the view function231 runs successfully, a commit is made; if the viewfunc produces an exception,232 a rollback is made. This is one of the most common ways to do transaction233 control in web apps.234 """235 def _commit_on_success(*args, **kw):236 try:237 enter_transaction_management()238 managed(True)239 try:240 res = func(*args, **kw)241 except:242 # All exceptions must be handled here (even string ones).243 if is_dirty():244 rollback()245 raise246 else:247 if is_dirty():248 commit()249 return res250 finally:251 leave_transaction_management()252 return wraps(func)(_commit_on_success)253 254 def commit_manually(func):255 """256 Decorator that activates manual transaction control. It just disables257 automatic transaction control and doesn't do any commit/rollback of its258 own -- it's up to the user to call the commit and rollback functions259 themselves.260 """261 def _commit_manually(*args, **kw):262 try:263 enter_transaction_management()264 managed(True)265 return func(*args, **kw)266 finally:267 leave_transaction_management()268 269 return wraps(func)(_commit_manually) -
django/conf/project_template/settings.py
16 16 DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. 17 17 DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. 18 18 19 20 DATABASE_TRANSACTION_POLICY = 'safe' # Options: 'safe','fast_select'; TODO: better doc. 21 19 22 # Local time zone for this installation. Choices can be found here: 20 23 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 21 24 # although not all choices may be available on all operating systems.