[CalendarServer-changes] [8725] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Fri Feb 17 13:16:48 PST 2012


Revision: 8725
          http://trac.macosforge.org/projects/calendarserver/changeset/8725
Author:   sagen at apple.com
Date:     2012-02-17 13:16:47 -0800 (Fri, 17 Feb 2012)
Log Message:
-----------
Adds a memcache lock around adding/removing sharing invitations.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/sharing.py
    CalendarServer/trunk/twistedcaldav/storebridge.py

Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/sharing.py	2012-02-17 17:42:16 UTC (rev 8724)
+++ CalendarServer/trunk/twistedcaldav/sharing.py	2012-02-17 21:16:47 UTC (rev 8725)
@@ -40,6 +40,7 @@
 from twistedcaldav.customxml import calendarserver_namespace
 from twistedcaldav.directory.wiki import WikiDirectoryService, getWikiAccess
 from twistedcaldav.linkresource import LinkFollowerMixIn
+from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
 from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
 
 from pycalendar.datetime import PyCalendarDateTime
@@ -478,7 +479,40 @@
             return results if resultIsList else results[0]
         return DeferredList(dl).addCallback(_defer)
         
+
     @inlineCallbacks
+    def _createLock(self, userid, request):
+        """
+        Create an instance of MemcacheLock whose key is based on the sharee's
+        uid and the collection's URL
+        """
+        returnValue(MemcacheLock(
+            "ShareInviteLock",
+            (yield self._lockToken(userid, request)),
+            timeout=config.Scheduling.Options.UIDLockTimeoutSeconds,
+            expire_time=config.Scheduling.Options.UIDLockExpirySeconds,
+        ))
+
+    @inlineCallbacks
+    def _acquireLock(self, lock):
+        """
+        Attempt to acquire a lock -- can raise MemcacheLockTimeoutError
+        """
+        try:
+            yield lock.acquire()
+        except MemcacheLockTimeoutError:
+            self.log_error("Memcache lock timeout for sharing invite")
+            raise
+
+    @inlineCallbacks
+    def _lockToken(self, userid, request):
+        """
+        Generate a string we can use for a memcache lock key
+        """
+        hosturl = (yield self.canonicalURL(request))
+        returnValue("%s:%s" % (hosturl, userid))
+
+    @inlineCallbacks
     def inviteSingleUserToShare(self, userid, cn, ace, summary, request):
         
         # Validate userid and cn
@@ -488,33 +522,54 @@
         if principalURL is None:
             returnValue(False)
 
-        # Look for existing invite and update its fields or create new one
-        principalUID = principalURL.split("/")[3]
-        record = yield self.invitesDB().recordForPrincipalUID(principalUID)
-        if record:
-            record.name = cn
-            record.access = inviteAccessMapFromXML[type(ace)]
-            record.summary = summary
-        else:
-            record = Invite(str(uuid4()), userid, principalUID, cn, inviteAccessMapFromXML[type(ace)], "NEEDS-ACTION", summary)
-        
-        # Send invite
-        yield self.sendInvite(record, request)
-        
-        # Add to database
-        yield self.invitesDB().addOrUpdateRecord(record)
-        
-        returnValue(True)            
+        # Acquire a memcache lock based on collection URL and sharee UID
+        # TODO: when sharing moves into the store this should be replaced
+        # by DB-level locking
+        lock = (yield self._createLock(userid, request))
+        yield self._acquireLock(lock)
 
+        try:
+            # Look for existing invite and update its fields or create new one
+            principalUID = principalURL.split("/")[3]
+            record = yield self.invitesDB().recordForPrincipalUID(principalUID)
+            if record:
+                record.name = cn
+                record.access = inviteAccessMapFromXML[type(ace)]
+                record.summary = summary
+            else:
+                record = Invite(str(uuid4()), userid, principalUID, cn, inviteAccessMapFromXML[type(ace)], "NEEDS-ACTION", summary)
 
+            # Send invite
+            yield self.sendInvite(record, request)
+
+            # Add to database
+            yield self.invitesDB().addOrUpdateRecord(record)
+
+        finally:
+            lock.clean()
+
+        returnValue(True)
+
+
     @inlineCallbacks
     def uninviteSingleUserFromShare(self, userid, aces, request):
         # Cancel invites - we'll just use whatever userid we are given
-        record = yield self.invitesDB().recordForUserID(userid)
-        if record:
-            result = (yield self.uninviteRecordFromShare(record, request))
-        else:
-            result = False
+
+        # Acquire a memcache lock based on collection URL and sharee UID
+        # TODO: when sharing moves into the store this should be replaced
+        # by DB-level locking
+        lock = (yield self._createLock(userid, request))
+        yield self._acquireLock(lock)
+
+        try:
+            record = yield self.invitesDB().recordForUserID(userid)
+            if record:
+                result = (yield self.uninviteRecordFromShare(record, request))
+            else:
+                result = False
+        finally:
+            lock.clean()
+
         returnValue(result)
 
 
@@ -772,8 +827,8 @@
             ))
 
         root = doc.root_element
-        if type(root) in self.xmlDocHanders:
-            result = (yield self.xmlDocHanders[type(root)](self, request, root))
+        if type(root) in self.xmlDocHandlers:
+            result = (yield self.xmlDocHandlers[type(root)](self, request, root))
             returnValue(result)
         else:
             self.log_error("Unsupported XML (%s)" % (root,))
@@ -783,7 +838,7 @@
                 "Unsupported XML",
             ))
 
-    xmlDocHanders = {
+    xmlDocHandlers = {
         customxml.InviteShare: _xmlHandleInvite,
         customxml.InviteReply: _xmlHandleInviteReply,          
     }

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2012-02-17 17:42:16 UTC (rev 8724)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2012-02-17 21:16:47 UTC (rev 8725)
@@ -986,7 +986,7 @@
 
         if config.EnableBatchUpload:
             self._postHandlers[("text", "calendar")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
-            self.xmlDocHanders[customxml.Multiput] = _CommonHomeChildCollectionMixin.crudBatchPOST
+            self.xmlDocHandlers[customxml.Multiput] = _CommonHomeChildCollectionMixin.crudBatchPOST
 
     def __repr__(self):
         return "<Calendar Collection Resource %r:%r %s>" % (
@@ -2001,7 +2001,7 @@
 
         if config.EnableBatchUpload:
             self._postHandlers[("text", "vcard")] = _CommonHomeChildCollectionMixin.simpleBatchPOST
-            self.xmlDocHanders[customxml.Multiput] = _CommonHomeChildCollectionMixin.crudBatchPOST
+            self.xmlDocHandlers[customxml.Multiput] = _CommonHomeChildCollectionMixin.crudBatchPOST
 
     def __repr__(self):
         return "<AddressBook Collection Resource %r:%r %s>" % (
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120217/dd80629e/attachment-0001.html>


More information about the calendarserver-changes mailing list