[Zodb-checkins] CVS: StandaloneZODB/ZODB - Transaction.py:1.36
Jeremy Hylton
jeremy@zope.com
Fri, 12 Apr 2002 15:59:56 -0400
Update of /cvs-repository/StandaloneZODB/ZODB
In directory cvs.zope.org:/tmp/cvs-serv26318
Modified Files:
Transaction.py
Log Message:
Refactor the commit() method.
Break up the logic into a bunch of helper methods:
_commit_objects()
_commit_subtrans()
_finish_one()
_finish_rest()
and possibly
_commit_error()
As a result of the changes, the high-level logic of a commit fits into
28 lines inside a try/finally. There are lots of details hidden in
the methods, but the names capture the high-level behavior of the
helpers.
=== StandaloneZODB/ZODB/Transaction.py 1.35 => 1.36 ===
'Finalize the transaction'
- global hosed
-
- objects=self._objects
- jars={}
+ objects = self._objects
+ jars = {}
jarsv = None
- subj=self._sub
- subjars=()
+ subj = self._sub
+ subjars = ()
if subtransaction:
- if subj is None: self._sub=subj={}
+ if subj is None:
+ self._sub = subj = {}
else:
if subj is not None:
if objects:
# Do an implicit sub-transaction commit:
self.commit(1)
- objects=[]
- subjars=subj.values()
- self._sub=None
+ # XXX What does this do?
+ objects = []
+ subjars = subj.values()
+ self._sub = None
# If not a subtransaction, then we need to add any non-
# subtransaction-supporting objects that may have been
# stowed away during subtransaction commits to _objects.
if (subtransaction is None) and (self._non_st_objects is not None):
- append=objects.append
- for object in self._non_st_objects:
- append(object)
+ objects.extend(self._non_st_objects)
self._non_st_objects = None
- t=v=tb=None
-
if (objects or subjars) and hosed:
# Something really bad happened and we don't
# trust the system state.
- raise POSException.TransactionError, (
-
- """A serious error, which was probably a system error,
- occurred in a previous database transaction. This
- application may be in an invalid state and must be
- restarted before database updates can be allowed.
-
- Beware though that if the error was due to a serious
- system problem, such as a disk full condition, then
- the application may not come up until you deal with
- the system problem. See your application log for
- information on the error that lead to this problem.
- """)
+ raise POSException.TransactionError, hosed_msg
+ # It's important that:
+ #
+ # - Every object in self._objects is either committed or
+ # aborted.
+ #
+ # - For each object that is committed we call tpc_begin on
+ # it's jar at least once
+ #
+ # - For every jar for which we've called tpc_begin on, we
+ # either call tpc_abort or tpc_finish. It is OK to call
+ # these multiple times, as the storage is required to ignore
+ # these calls if tpc_begin has not been called.
try:
-
- # It's important that:
- #
- # - Every object in self._objects is either committed
- # or aborted.
- #
- # - For each object that is committed
- # we call tpc_begin on it's jar at least once
- #
- # - For every jar for which we've called tpc_begin on,
- # we either call tpc_abort or tpc_finish. It is OK
- # to call these multiple times, as the storage is
- # required to ignore these calls if tpc_begin has not
- # been called.
-
- ncommitted=0
+ ncommitted = 0
try:
- for o in objects:
- j=getattr(o, '_p_jar', o)
- if j is not None:
- i=id(j)
- if not jars.has_key(i):
- jars[i]=j
- if subtransaction:
-
- # If a jar does not support subtransactions,
- # we need to save it away to be committed in
- # the outer transaction.
- try: j.tpc_begin(self, subtransaction)
- except TypeError:
- j.tpc_begin(self)
-
- if hasattr(j, 'commit_sub'):
- subj[i]=j
- else:
- if self._non_st_objects is None:
- self._non_st_objects = []
- self._non_st_objects.append(o)
- continue
-
- else:
- j.tpc_begin(self)
- j.commit(o,self)
- ncommitted=ncommitted+1
-
- # Commit work done in subtransactions
- while subjars:
- j=subjars.pop()
- i=id(j)
- if not jars.has_key(i):
- jars[i]=j
-
- j.commit_sub(self)
+ ncommitted += self._commit_objects(objects, jars,
+ subtransaction, subj)
+
+ self._commit_subtrans(jars, subjars)
jarsv = jars.values()
for jar in jarsv:
if not subtransaction:
- try: jar=jar.tpc_vote
- except: pass
- else: jar(self) # last chance to bail
-
- try:
- # Try to finish one jar, since we may be able to
- # recover if the first one fails.
- if jarsv:
- jarsv[-1].tpc_finish(self) # This should never fail
- jarsv.pop() # It didn't, so it's taken care of.
- except:
- # Bug if it does, we need to keep track of it
- LOG('ZODB', ERROR,
- "A storage error occurred in the last phase of a "
- "two-phase commit. This shouldn\'t happen. ",
- error=sys.exc_info())
- raise
-
- try:
- while jarsv:
- jarsv[-1].tpc_finish(self) # This should never fail
- jarsv.pop() # It didn't, so it's taken care of.
- except:
- # Bug if it does, we need to yell FIRE!
- # Someone finished, so don't allow any more
- # work without at least a restart!
- hosed = 1
- LOG('ZODB', PANIC,
- "A storage error occurred in the last phase of a "
- "two-phase commit. This shouldn\'t happen. "
- "The application may be in a hosed state, so "
- "transactions will not be allowed to commit "
- "until the site/storage is reset by a restart. ",
- error=sys.exc_info())
- raise
-
+ try:
+ vote = jar.tpc_vote
+ except:
+ pass
+ else:
+ vote(self) # last chance to bail
+
+ # Try to finish one jar, since we may be able to
+ # recover if the first one fails.
+ self._finish_one(jarsv)
+ # Once a single jar has finished, it's a fatal (hosed)
+ # error if another jar fails.
+ self._finish_rest(jarsv)
except:
- t, v, tb = sys.exc_info()
-
# Ugh, we got an got an error during commit, so we
# have to clean up.
-
- # First, we have to abort any uncommitted objects.
- for o in objects[ncommitted:]:
- try:
- j = getattr(o, '_p_jar', o)
- if j is not None:
- j.abort(o, self)
- except:
- pass
-
- # Then, we unwind TPC for the jars that began it.
+ exc_info = sys.exc_info()
if jarsv is None:
jarsv = jars.values()
- for j in jarsv:
- try:
- j.tpc_abort(self) # This should never fail
- except:
- LOG('ZODB', ERROR,
- "A storage error occured during object abort "
- "This shouldn't happen. ",
- error=sys.exc_info())
-
- # Ugh, we need to abort work done in sub-transactions.
- while subjars:
- j = subjars.pop()
- try:
- j.abort_sub(self) # This should never fail
- except:
- LOG('ZODB', ERROR,
- "A storage error occured during sub-transaction "
- "object abort. This shouldn't happen.",
- error=sys.exc_info())
-
- raise t, v, tb
-
+ self._commit_error(exc_info, objects, ncommitted,
+ jarsv, subjars)
finally:
- tb = None
del objects[:] # clear registered
if not subtransaction and self._id is not None:
free_transaction()
+ def _commit_objects(self, objects, jars, subtransaction, subj):
+ # commit objects and return number of commits
+ ncommitted = 0
+ for o in objects:
+ j = getattr(o, '_p_jar', o)
+ if j is not None:
+ i = id(j)
+ if not jars.has_key(i):
+ jars[i] = j
+
+ if subtransaction:
+ # If a jar does not support subtransactions,
+ # we need to save it away to be committed in
+ # the outer transaction.
+ try:
+ j.tpc_begin(self, subtransaction)
+ except TypeError:
+ j.tpc_begin(self)
+
+ if hasattr(j, 'commit_sub'):
+ subj[i] = j
+ else:
+ if self._non_st_objects is None:
+ self._non_st_objects = []
+ self._non_st_objects.append(o)
+ continue
+ else:
+ j.tpc_begin(self)
+ j.commit(o, self)
+ ncommitted += 1
+ return ncommitted
+
+ def _commit_subtrans(self, jars, subjars):
+ # Commit work done in subtransactions
+ while subjars:
+ j = subjars.pop()
+ i = id(j)
+ if not jars.has_key(i):
+ jars[i] = j
+ j.commit_sub(self)
+
+ def _finish_one(self, jarsv):
+ try:
+ if jarsv:
+ jarsv[-1].tpc_finish(self) # This should never fail
+ jarsv.pop() # It didn't, so it's taken care of.
+ except:
+ # Bug if it does, we need to keep track of it
+ LOG('ZODB', ERROR,
+ "A storage error occurred in the last phase of a "
+ "two-phase commit. This shouldn\'t happen. ",
+ error=sys.exc_info())
+ raise
+
+ def _finish_rest(self, jarsv):
+ global hosed
+ try:
+ while jarsv:
+ jarsv[-1].tpc_finish(self) # This should never fail
+ jarsv.pop() # It didn't, so it's taken care of.
+ except:
+ # Bug if it does, we need to yell FIRE!
+ # Someone finished, so don't allow any more
+ # work without at least a restart!
+ hosed = 1
+ LOG('ZODB', PANIC,
+ "A storage error occurred in the last phase of a "
+ "two-phase commit. This shouldn\'t happen. "
+ "The application may be in a hosed state, so "
+ "transactions will not be allowed to commit "
+ "until the site/storage is reset by a restart. ",
+ error=sys.exc_info())
+ raise
+
+ def _commit_error(self, (t, v, tb),
+ objects, ncommitted, jarsv, subjars):
+ # handle an exception raised during commit
+ # takes sys.exc_info() as argument
+
+ # First, we have to abort any uncommitted objects.
+ for o in objects[ncommitted:]:
+ try:
+ j = getattr(o, '_p_jar', o)
+ if j is not None:
+ j.abort(o, self)
+ except:
+ pass
+
+ # Then, we unwind TPC for the jars that began it.
+ for j in jarsv:
+ try:
+ j.tpc_abort(self) # This should never fail
+ except:
+ LOG('ZODB', ERROR,
+ "A storage error occured during object abort. This "
+ "shouldn't happen. ", error=sys.exc_info())
+
+ # Ugh, we need to abort work done in sub-transactions.
+ while subjars:
+ j = subjars.pop()
+ try:
+ j.abort_sub(self) # This should never fail
+ except:
+ LOG('ZODB', ERROR,
+ "A storage error occured during sub-transaction "
+ "object abort. This shouldn't happen.",
+ error=sys.exc_info())
+
+ raise t, v, tb
+
def register(self,object):
'Register the given object for transaction control.'
self._append(object)
@@ -350,6 +353,20 @@
if ext is None:
ext=self._extension={}
ext[name]=value
+
+hosed_msg = \
+"""A serious error, which was probably a system error,
+occurred in a previous database transaction. This
+application may be in an invalid state and must be
+restarted before database updates can be allowed.
+
+Beware though that if the error was due to a serious
+system problem, such as a disk full condition, then
+the application may not come up until you deal with
+the system problem. See your application log for
+information on the error that lead to this problem.
+"""
+
############################################################################