[CalendarServer-changes] [9970] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Oct 22 16:11:21 PDT 2012


Revision: 9970
          http://trac.calendarserver.org//changeset/9970
Author:   glyph at apple.com
Date:     2012-10-22 16:11:21 -0700 (Mon, 22 Oct 2012)
Log Message:
-----------
Always abort transactions for HTTP requests that end in an exception being raised, even if that exception is an expected HTTPError that indicates some kind of known error.

Modified Paths:
--------------
    CalendarServer/trunk/twext/web2/resource.py
    CalendarServer/trunk/twistedcaldav/method/mkcalendar.py
    CalendarServer/trunk/twistedcaldav/method/mkcol.py
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/storebridge.py
    CalendarServer/trunk/twistedcaldav/test/test_wrapping.py
    CalendarServer/trunk/txdav/base/propertystore/sql.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/test/test_sql.py

Property Changed:
----------------
    CalendarServer/trunk/


Property changes on: CalendarServer/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593

Modified: CalendarServer/trunk/twext/web2/resource.py
===================================================================
--- CalendarServer/trunk/twext/web2/resource.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/twext/web2/resource.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -34,6 +34,7 @@
 
 from twext.web2 import iweb, http, server, responsecode
 
+from twisted.internet.defer import maybeDeferred
 class RenderMixin(object):
     """
     Mix-in class for L{iweb.IResource} which provides a dispatch mechanism for
@@ -106,9 +107,20 @@
             returnValue(response)
 
         yield self.checkPreconditions(request)
-        returnValue((yield method(request)))
+        result = maybeDeferred(method, request)
+        result.addErrback(self.methodRaisedException)
+        returnValue((yield result))
 
 
+    def methodRaisedException(self, failure):
+        """
+        An C{http_METHOD} method raised an exception; this is an errback for
+        that exception.  By default, simply propagate the error up; subclasses
+        may override this for top-level exception handling.
+        """
+        return failure
+
+
     def http_OPTIONS(self, request):
         """
         Respond to a OPTIONS request.

Modified: CalendarServer/trunk/twistedcaldav/method/mkcalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/mkcalendar.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/twistedcaldav/method/mkcalendar.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -106,7 +106,6 @@
     
         if got_an_error:
             # Force a transaction error and proper clean-up
-            self.transactionError()
             errors.error()
             raise HTTPError(MultiStatusResponse([errors.response()]))
         

Modified: CalendarServer/trunk/twistedcaldav/method/mkcol.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/mkcol.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/twistedcaldav/method/mkcol.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -182,7 +182,6 @@
 
         if got_an_error:
             # Clean up
-            self.transactionError()
             errors.error()
             raise HTTPError(Response(
                     code=responsecode.FORBIDDEN,

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -318,8 +318,14 @@
         otherResource.associateWithTransaction(self._associatedTransaction)
 
 
-    def transactionError(self):
+    def methodRaisedException(self, failure):
+        """
+        An C{http_METHOD} method raised an exception.  Any type of exception,
+        including those that result in perfectly valid HTTP responses, should
+        abort the transaction.
+        """
         self._transactionError = True
+        return super(CalDAVResource, self).methodRaisedException(failure)
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -1611,8 +1611,6 @@
             yield readStream(request.stream, t.write)
         except Exception, e:
             log.error("Unable to store attachment: %s" % (e,))
-            # Signal to abort in twistedcaldav.resource.CalDAVResource.RenderHTTP
-            self.transactionError()
             raise HTTPError(SERVICE_UNAVAILABLE)
 
         try:

Modified: CalendarServer/trunk/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_wrapping.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/twistedcaldav/test/test_wrapping.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -53,6 +53,7 @@
 from twistedcaldav.directory.test.test_xmlfile import XMLFileBase
 from txdav.caldav.icalendarstore import ICalendarHome
 from txdav.carddav.iaddressbookstore import IAddressBookHome
+
 from txdav.caldav.datastore.file import Calendar
 
 
@@ -108,6 +109,7 @@
 
         @param objectName: The name of a calendar object.
         @type objectName: str
+
         @param objectText: Some iCalendar text to populate it with.
         @type objectText: str
         """
@@ -167,7 +169,6 @@
 
         @param path: the path from the root of the site (not starting with a
             slash)
-
         @type path: C{str}
 
         @param method: the HTTP method to initialize the request with.
@@ -266,8 +267,8 @@
 
     def test_createStore(self):
         """
-        Creating a DirectoryCalendarHomeProvisioningResource will create a paired
-        CalendarStore.
+        Creating a DirectoryCalendarHomeProvisioningResource will create a
+        paired CalendarStore.
         """
         assertProvides(self, IDataStore, self.calendarCollection._newStore)
 
@@ -518,7 +519,20 @@
                           frozenset([self.principalsResource]))
 
 
+    @inlineCallbacks
+    def assertCalendarEmpty(self, user, calendarName="calendar"):
+        """
+        Assert that a user's calendar is empty (their default calendar by default).
+        """
+        txn = self.calendarStore.newTransaction()
+        self.addCleanup(txn.commit)
+        home = yield txn.calendarHomeWithUID(user, create=True)
+        cal = yield home.calendarWithName(calendarName)
+        objects = yield cal.calendarObjects()
+        self.assertEquals(len(objects), 0)
 
+
+
 class DatabaseWrappingTests(WrappingTests):
 
     @inlineCallbacks
@@ -531,4 +545,73 @@
         return self.calendarStore
 
 
+    @inlineCallbacks
+    def test_invalidCalendarPUT(self):
+        """
+        Exceeding quota on an attachment returns an HTTP error code.
+        """
+        # yield self.populateOneObject("1.ics", test_event_text)
+        @inlineCallbacks
+        def putEvt(txt):
+            calendarObject = yield self.getResource(
+                "/calendars/users/wsanchez/calendar/1.ics",
+                "PUT", "wsanchez"
+            )
+            self.requestUnderTest.stream = MemoryStream(txt)
+            returnValue(
+                ((yield calendarObject.renderHTTP(self.requestUnderTest)),
+                 self.requestUnderTest)
+            )
+        # see twistedcaldav/directory/test/accounts.xml
+        wsanchez = '6423F94A-6B76-4A3A-815B-D52CFD77935D'
+        cdaboo = '5A985493-EE2C-4665-94CF-4DFEA3A89500'
+        eventTemplate="""\
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73
+ORGANIZER:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{wsanchez}
+DTSTART:20110101T050000Z
+DTSTAMP:20110309T185105Z
+DURATION:PT1H
+SUMMARY:Test
+RRULE:FREQ=DAILY;COUNT=2
+END:VEVENT
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73
+RECURRENCE-ID:20110102T050000Z
+ORGANIZER:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{cdaboo}
+DTSTART:20110102T050000Z
+DTSTAMP:20110309T185105Z
+DURATION:PT1H
+SUMMARY:Test
+END:VEVENT{0}
+END:VCALENDAR
+"""
+        CR = "\n"
+        CRLF = "\r\n"
+        #validEvent = eventTemplate.format("", wsanchez=wsanchez, cdaboo=cdaboo).replace(CR, CRLF)
+        invalidInstance = """
+BEGIN:VEVENT
+UID:20060110T231240Z-4011c71-187-6f73
+RECURRENCE-ID:20110110T050000Z
+ORGANIZER:urn:uuid:{wsanchez}
+ATTENDEE:urn:uuid:{wsanchez}
+DTSTART:20110110T050000Z
+DTSTAMP:20110309T185105Z
+DURATION:PT1H
+SUMMARY:Test
+END:VEVENT""".format(wsanchez=wsanchez, cdaboo=cdaboo)
+        #txn = self.requestUnderTest._newStoreTransaction
+        invalidEvent = eventTemplate.format(invalidInstance, wsanchez=wsanchez, cdaboo=cdaboo).replace(CR, CRLF)
+        resp2, rsrc2 = yield putEvt(invalidEvent)
+        self.requestUnderTest = None
+        yield self.assertCalendarEmpty(wsanchez)
+        yield self.assertCalendarEmpty(cdaboo)
 
+

Modified: CalendarServer/trunk/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/base/propertystore/sql.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/txdav/base/propertystore/sql.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -30,6 +30,8 @@
     Select, Parameter, Update, Insert, TableSyntax, Delete)
 
 from txdav.xml.parser import WebDAVDocument
+from txdav.common.icommondatastore import AllRetriesFailed
+from twext.python.log import LoggingMixIn
 from txdav.common.datastore.sql_tables import schema
 from txdav.base.propertystore.base import (AbstractPropertyStore,
                                            PropertyName, validKey)
@@ -39,7 +41,7 @@
 
 prop = schema.RESOURCE_PROPERTY
 
-class PropertyStore(AbstractPropertyStore):
+class PropertyStore(AbstractPropertyStore, LoggingMixIn):
 
     _cacher = Memcacher("SQL.props", pickle=True, key_normalization=False)
 
@@ -255,7 +257,10 @@
         if hasattr(self, "_notifyCallback") and self._notifyCallback is not None:
             self._notifyCallback()
 
-        self._txn.subtransaction(trySetItem)
+        def justLogIt(f):
+            f.trap(AllRetriesFailed)
+            self.log_error("setting a property failed; probably nothing.")
+        self._txn.subtransaction(trySetItem).addErrback(justLogIt)
 
 
 

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -751,6 +751,13 @@
         block = self._sqlTxn.commandBlock()
         sp = self._savepoint()
         failuresToMaybeLog = []
+        def end():
+            block.end()
+            for f in failuresToMaybeLog:
+                # TODO: direct tests, to make sure error logging
+                # happens correctly in all cases.
+                log.err(f)
+            raise AllRetriesFailed()
         triesLeft = retries
         try:
             while True:
@@ -758,8 +765,9 @@
                 try:
                     result = yield thunk(block)
                 except:
+                    f = Failure()
                     if not failureOK:
-                        failuresToMaybeLog.append(Failure())
+                        failuresToMaybeLog.append(f)
                     yield sp.rollback(block)
                     if triesLeft:
                         triesLeft -= 1
@@ -774,12 +782,7 @@
                         block = newBlock
                         sp = self._savepoint()
                     else:
-                        block.end()
-                        for f in failuresToMaybeLog:
-                            # TODO: direct tests, to make sure error logging
-                            # happens correctly in all cases.
-                            log.err(f)
-                        raise AllRetriesFailed()
+                        end()
                 else:
                     yield sp.release(block)
                     block.end()
@@ -790,9 +793,10 @@
             # and only that case - acquire() or release() or commandBlock() may
             # raise an AlreadyFinishedError (either synchronously, or in the
             # case of the first two, possibly asynchronously as well).  We can
-            # safely ignore this, because it can't have any real effect; our
-            # caller shouldn't be paying attention anyway.
-            block.end()
+            # safely ignore this error, because it can't have any effect on what
+            # gets written; our caller will just get told that it failed in a
+            # way they have to be prepared for anyway.
+            end()
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/txdav/common/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/test_sql.py	2012-10-22 23:09:18 UTC (rev 9969)
+++ CalendarServer/trunk/txdav/common/datastore/test/test_sql.py	2012-10-22 23:11:21 UTC (rev 9970)
@@ -24,6 +24,7 @@
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.internet.task import Clock
 from twisted.trial.unittest import TestCase
+from twisted.internet.defer import Deferred
 
 from txdav.common.datastore.sql import log, CommonStoreTransactionMonitor,\
     CommonHome, CommonHomeChild, ECALENDARTYPE
@@ -227,7 +228,8 @@
     @inlineCallbacks
     def test_subtransactionFailSomeRetries(self):
         """
-        txn.subtransaction runs loop three times when all fail and two retries requested.
+        txn.subtransaction runs loop three times when all fail and two retries
+        requested.
         """
         
         txn = self.transactionUnderTest()
@@ -251,7 +253,35 @@
             self.fail("AllRetriesFailed not raised")
         self.assertEqual(ctr[0], 3)
 
+
     @inlineCallbacks
+    def test_subtransactionAbortOuterTransaction(self):
+        """
+        If an outer transaction that is holding a subtransaction open is
+        aborted, then the L{Deferred} returned by L{subtransaction} raises
+        L{AllRetriesFailed}.
+        """
+        txn = self.transactionUnderTest()
+        cs = schema.CALENDARSERVER
+        waitAMoment = Deferred()
+        @inlineCallbacks
+        def later(subtxn):
+            yield waitAMoment
+            value = yield Select([cs.VALUE], From=cs).on(subtxn)
+            returnValue(value)
+        started = txn.subtransaction(later)
+        txn.abort()
+        waitAMoment.callback(True)
+        try:
+            result = yield started
+        except AllRetriesFailed:
+            pass
+        else:
+            self.fail("AllRetriesFailed not raised, %r returned instead" %
+                      (result,))
+
+
+    @inlineCallbacks
     def test_changeRevision(self):
         """
         CommonHomeChild._changeRevision actions.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121022/c8c2047f/attachment-0001.html>


More information about the calendarserver-changes mailing list