[CalendarServer-changes] [7254] CalendarServer/branches/users/glyph/subtransactions/twext/enterprise

source_changes at macosforge.org source_changes at macosforge.org
Thu Mar 24 13:36:30 PDT 2011


Revision: 7254
          http://trac.macosforge.org/projects/calendarserver/changeset/7254
Author:   glyph at apple.com
Date:     2011-03-24 13:36:29 -0700 (Thu, 24 Mar 2011)
Log Message:
-----------
more edge cases; block commit, don't block abort, raise appropriate exceptions when invoked from the wrong place.

Modified Paths:
--------------
    CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/adbapi2.py
    CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/test/test_adbapi2.py

Modified: CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/adbapi2.py
===================================================================
--- CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/adbapi2.py	2011-03-24 20:36:18 UTC (rev 7253)
+++ CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/adbapi2.py	2011-03-24 20:36:29 UTC (rev 7254)
@@ -356,16 +356,19 @@
 
     This is the only L{IAsyncTransaction} implementation exposed to application
     code.
+
+    It's also the only implementor of the C{commandBlock} method for grouping
+    commands together.
     """
 
     def __init__(self, pool, baseTxn):
-        self._pool          = pool
-        self._baseTxn       = baseTxn
-        self._complete      = False
-        self._currentBlock  = None
-        self._pendingBlocks = []
-        self._stillExecuting    = []
-        self._blockedQueue  = None
+        self._pool           = pool
+        self._baseTxn        = baseTxn
+        self._complete       = False
+        self._currentBlock   = None
+        self._blockedQueue   = None
+        self._pendingBlocks  = []
+        self._stillExecuting = []
 
 
     def __repr__(self):
@@ -451,6 +454,11 @@
 
 
     def commit(self):
+        if self._blockedQueue is not None:
+            # We're in the process of executing a block of commands.  Wait until
+            # they're done.  (Commit will be repeated in _checkNextBlock.)
+            return self._blockedQueue.commit()
+
         self._markComplete()
         return super(_SingleTxn, self).commit()
 
@@ -492,6 +500,7 @@
         Create a L{CommandBlock} which will wait for all currently spooled
         commands to complete before executing its own.
         """
+        self._checkComplete()
         block = CommandBlock(self)
         if self._currentBlock is None:
             self._blockedQueue = _WaitingTxn(self._pool)
@@ -546,8 +555,20 @@
     def execSQL(self, sql, args=None, raiseOnZeroRowCount=None, track=True):
         """
         Execute some SQL within this command block.
+
+        @param sql: the SQL string to execute.
+
+        @param args: the SQL arguments.
+
+        @param raiseOnZeroRowCount: see L{IAsyncTransaction.execSQL}
+
+        @param track: an internal parameter; was this called by application code
+            or as part of unspooling some previously-queued requests?  True if
+            application code, False if unspooling.
         """
-        # FIXME: check 'ended'
+        if track and self._ended:
+            raise AlreadyFinishedError()
+        self._singleTxn._checkComplete()
         if self._singleTxn._currentBlock is self and self._started:
             d = self._singleTxn._execSQLForBlock(
                 sql, args, raiseOnZeroRowCount, self)
@@ -561,7 +582,8 @@
     def _trackForEnd(self, d):
         """
         Watch the following L{Deferred}, since we need to watch it to determine
-        when C{end} should be considered done.
+        when C{end} should be considered done, and the next CommandBlock or
+        regular SQL statement should be unqueued.
         """
         self._waitingForEnd.append(d)
 
@@ -576,6 +598,9 @@
         if self._ended:
             raise AlreadyFinishedError()
         self._ended = True
+        # TODO: maybe this should return a Deferred that's a clone of
+        # _endDeferred, so that callers can determine when the block is really
+        # complete?  Struggling for an actual use-case on that one.
         DeferredList(self._waitingForEnd).chainDeferred(self._endDeferred)
 
 
@@ -734,6 +759,8 @@
         @return: an L{IAsyncTransaction}
         """
         if self._stopping:
+            # FIXME: should be wrapping a _SingleTxn around this to get
+            # .commandBlock()
             return _NoTxn(self)
         if self._free:
             basetxn = self._free.pop(0)

Modified: CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/test/test_adbapi2.py
===================================================================
--- CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/test/test_adbapi2.py	2011-03-24 20:36:18 UTC (rev 7253)
+++ CalendarServer/branches/users/glyph/subtransactions/twext/enterprise/test/test_adbapi2.py	2011-03-24 20:36:29 UTC (rev 7254)
@@ -959,3 +959,81 @@
         block.end()
         self.assertRaises(AlreadyFinishedError, block.end)
 
+
+    def test_commandBlockDelaysCommit(self):
+        """
+        Some command blocks need to run asynchronously, without the overall
+        transaction-managing code knowing how far they've progressed.  Therefore
+        when you call {IAsyncTransaction.commit}(), it should not actually take
+        effect if there are any pending command blocks.
+        """
+        txn = self.pool.connection()
+        block = txn.commandBlock()
+        commitResult = resultOf(txn.commit())
+        block.execSQL("in block")
+        self.assertEquals(commitResult, [])
+        self.assertEquals(self.factory.connections[0].cursors[0].allExecutions,
+                          [("in block", [])])
+        block.end()
+        self.assertEquals(commitResult, [None])
+
+
+    def test_commandBlockDoesntDelayAbort(self):
+        """
+        A L{CommandBlock} can't possibly have anything interesting to say about
+        a transaction that gets rolled back, so C{abort} applies immediately;
+        all outstanding C{execSQL}s will fail immediately, on both command
+        blocks and on the transaction itself.
+        """
+        txn = self.pool.connection()
+        block = txn.commandBlock()
+        block2 = txn.commandBlock()
+        abortResult = resultOf(txn.abort())
+        self.assertEquals(abortResult, [None])
+        self.assertRaises(AlreadyFinishedError, block2.execSQL, "bar")
+        self.assertRaises(AlreadyFinishedError, block.execSQL, "foo")
+        self.assertRaises(AlreadyFinishedError, txn.execSQL, "baz")
+        self.assertEquals(self.factory.connections[0].cursors[0].allExecutions,
+                          [])
+        # end() should _not_ raise an exception, because this is the sort of
+        # thing that might be around a try/finally or try/except; it's just
+        # putting the commandBlock itself into a state consistent with the
+        # transaction.
+        block.end()
+        block2.end()
+
+
+    def test_endedBlockDoesntExecuteMoreSQL(self):
+        """
+        Attempting to execute SQL on a L{CommandBlock} which has had C{end}
+        called on it will result in an L{AlreadyFinishedError}.
+        """
+        txn = self.pool.connection()
+        block = txn.commandBlock()
+        block.end()
+        self.assertRaises(AlreadyFinishedError, block.execSQL, "hello")
+        self.assertEquals(self.factory.connections[0].cursors[0].allExecutions,
+                          [])
+
+
+    def test_commandBlockAfterCommitRaises(self):
+        """
+        Once an L{IAsyncTransaction} has been committed, L{commandBlock} raises
+        an exception.
+        """
+        txn = self.pool.connection()
+        txn.commit()
+        self.assertRaises(AlreadyFinishedError, txn.commandBlock)
+
+
+    def test_commandBlockAfterAbortRaises(self):
+        """
+        Once an L{IAsyncTransaction} has been committed, L{commandBlock} raises
+        an exception.
+        """
+        txn = self.pool.connection()
+        txn.abort()
+        self.assertRaises(AlreadyFinishedError, txn.commandBlock)
+
+
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110324/d6ac3a8e/attachment.html>


More information about the calendarserver-changes mailing list