[CalendarServer-changes] [5742] CalendarServer/branches/new-store

source_changes at macosforge.org source_changes at macosforge.org
Tue Jun 15 14:03:31 PDT 2010


Revision: 5742
          http://trac.macosforge.org/projects/calendarserver/changeset/5742
Author:   glyph at apple.com
Date:     2010-06-15 14:03:29 -0700 (Tue, 15 Jun 2010)
Log Message:
-----------
Guard write operations against use outside transactions, and provide a back-door hook for scheduling processing to re-initialize a resource for deferred processing.

Modified Paths:
--------------
    CalendarServer/branches/new-store/twistedcaldav/scheduling/processing.py
    CalendarServer/branches/new-store/twistedcaldav/storebridge.py
    CalendarServer/branches/new-store/txcaldav/calendarstore/file.py

Modified: CalendarServer/branches/new-store/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/scheduling/processing.py	2010-06-15 18:54:24 UTC (rev 5741)
+++ CalendarServer/branches/new-store/twistedcaldav/scheduling/processing.py	2010-06-15 21:03:29 UTC (rev 5742)
@@ -431,20 +431,27 @@
         # refresh them. To prevent a race we need a lock.
         lock = MemcacheLock("ImplicitUIDLock", calendar.resourceUID(), timeout=60.0)
 
+        # Note that this lock also protects the request, as this request is
+        # being re-used by potentially multiple transactions and should not be
+        # used concurrency (the locateResource cache needs to be cleared each
+        # time, by inNewTransaction). -glyph
         try:
             yield lock.acquire()
-
-            # Send out a reply
-            log.debug("ImplicitProcessing - recipient '%s' processing UID: '%s' - auto-reply: %s" % (self.recipient.cuaddr, self.uid, partstat))
-            from twistedcaldav.scheduling.implicit import ImplicitScheduler
-            scheduler = ImplicitScheduler()
-            yield scheduler.sendAttendeeReply(self.request, resource, calendar, self.recipient)
-
         except MemcacheLockTimeoutError:
-            
             # Just try again to get the lock
             reactor.callLater(2.0, self.sendAttendeeAutoReply, *(calendar, resource, partstat))
-    
+        else:
+            txn = resource.inNewTransaction(self.request)
+            try:
+                # Send out a reply
+                log.debug("ImplicitProcessing - recipient '%s' processing UID: '%s' - auto-reply: %s" % (self.recipient.cuaddr, self.uid, partstat))
+                from twistedcaldav.scheduling.implicit import ImplicitScheduler
+                scheduler = ImplicitScheduler()
+                yield scheduler.sendAttendeeReply(self.request, resource, calendar, self.recipient)
+            except:
+                txn.abort()
+            else:
+                txn.commit()
         finally:
             yield lock.clean()
 

Modified: CalendarServer/branches/new-store/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/storebridge.py	2010-06-15 18:54:24 UTC (rev 5741)
+++ CalendarServer/branches/new-store/twistedcaldav/storebridge.py	2010-06-15 21:03:29 UTC (rev 5742)
@@ -350,6 +350,31 @@
         self._initializeWithObject(calendarObject)
 
 
+    def inNewTransaction(self, request):
+        """
+        Implicit auto-replies need to span multiple transactions.  Clean out the
+        given request's resource-lookup mapping, transaction, and re-look-up my
+        calendar object in a new transaction.
+
+        Return the new transaction so it can be committed.
+        """
+        # FIXME: private names from 'file' implementation; maybe there should be
+        # a public way to do this?  or maybe we should just have a real queue.
+        objectName = self._newStoreObject.name()
+        calendarName = self._newStoreObject._calendar.name()
+        homeUID = self._newStoreObject._calendar._calendarHome.uid()
+        store = self._newStoreObject._transaction._calendarStore
+        txn = store.newTransaction()
+        newObject = (txn.calendarHomeWithUID(homeUID)
+                        .calendarWithName(calendarName)
+                        .calendarObjectWithName(objectName))
+        request._newStoreTransaction = txn
+        request._resourcesByURL.clear()
+        request._urlsByResource.clear()
+        self._initializeWithObject(newObject)
+        return txn
+
+
     def exists(self):
         # FIXME: Tests
         return True

Modified: CalendarServer/branches/new-store/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/new-store/txcaldav/calendarstore/file.py	2010-06-15 18:54:24 UTC (rev 5741)
+++ CalendarServer/branches/new-store/txcaldav/calendarstore/file.py	2010-06-15 21:03:29 UTC (rev 5742)
@@ -100,6 +100,17 @@
 
 
 
+def _writeOperation(thunk):
+    # FIXME: tests
+    def inner(self, *a, **kw):
+        if self._transaction._termination is not None:
+            raise RuntimeError(
+                "%s.%s is a write operation, but transaction already %s"
+                % (self, thunk.__name__, self._transaction._termination))
+        return thunk(self, *a, **kw)
+    return inner
+
+
 class CalendarStore(LoggingMixIn):
     """
     An implementation of L{ICalendarObject} backed by a
@@ -312,6 +323,7 @@
             return None
 
 
+    @_writeOperation
     def createCalendarWithName(self, name):
         if name.startswith("."):
             raise CalendarNameNotAllowedError(name)
@@ -362,7 +374,7 @@
         # c.properties().participateInTxn(txn)
         # FIXME: return c # maybe ?
 
-
+    @_writeOperation
     def removeCalendarWithName(self, name):
         if name.startswith(".") or name in self._removedCalendars:
             raise NoSuchCalendarError(name)
@@ -469,6 +481,7 @@
 
     _renamedName = None
 
+    @_writeOperation
     def rename(self, name):
         oldName = self.name()
         self._renamedName = name
@@ -527,6 +540,7 @@
                 return obj
 
 
+    @_writeOperation
     def createCalendarObjectWithName(self, name, component):
         if name.startswith("."):
             raise CalendarObjectNameNotAllowedError(name)
@@ -540,6 +554,7 @@
         self._cachedCalendarObjects[name] = calendarObject
 
 
+    @_writeOperation
     def removeCalendarObjectWithName(self, name):
         if name.startswith("."):
             raise NoSuchCalendarObjectError(name)
@@ -556,13 +571,16 @@
             raise NoSuchCalendarObjectError(name)
 
 
+    @_writeOperation
     def removeCalendarObjectWithUID(self, uid):
-        self.removeCalendarObjectWithName(self.calendarObjectWithUID(uid)._path.basename())
+        self.removeCalendarObjectWithName(
+            self.calendarObjectWithUID(uid)._path.basename())
 
 
     def syncToken(self):
         raise NotImplementedError()
 
+
     def _updateSyncToken(self, reset=False):
         # FIXME: add locking a-la CalDAVFile.bumpSyncToken
         # FIXME: tests for desired concurrency properties
@@ -588,6 +606,7 @@
         raise NotImplementedError()
 
 
+    # FIXME: property writes should be a write operation
     @_cached
     def properties(self):
         # FIXME: needs direct tests - only covered by calendar store tests
@@ -617,6 +636,7 @@
     def __init__(self, name, calendar):
         self._name = name
         self._calendar = calendar
+        self._transaction = calendar._transaction
         self._component = None
 
 
@@ -633,6 +653,7 @@
         return self._path.basename()
 
 
+    @_writeOperation
     def setComponent(self, component):
         if not isinstance(component, VComponent):
             raise TypeError(type(component))
@@ -673,7 +694,7 @@
                 else:
                     self._path.remove()
             return undo
-        self._calendar._transaction.addOperation(do)
+        self._transaction.addOperation(do)
         # Mark all properties as dirty, so they will be re-added to the
         # temporary file when the main file is deleted. NOTE: if there were a
         # temporary file and a rename() as there should be, this should really
@@ -684,7 +705,7 @@
         # happens _after_ the new file has been written.  we may end up doing
         # the work multiple times, and external callers to property-
         # manipulation methods won't work.
-        self._calendar._transaction.addOperation(self.properties().flush)
+        self._transaction.addOperation(self.properties().flush)
 
 
     def component(self):
@@ -745,7 +766,7 @@
     @_cached
     def properties(self):
         props = PropertyStore(self._path)
-        self._calendar._transaction.addOperation(props.flush)
+        self._transaction.addOperation(props.flush)
         return props
 
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100615/c37a01fe/attachment-0001.html>


More information about the calendarserver-changes mailing list