[CalendarServer-changes] [7185] CalendarServer/trunk/txdav

source_changes at macosforge.org source_changes at macosforge.org
Fri Mar 11 13:10:33 PST 2011


Revision: 7185
          http://trac.macosforge.org/projects/calendarserver/changeset/7185
Author:   cdaboo at apple.com
Date:     2011-03-11 13:10:33 -0800 (Fri, 11 Mar 2011)
Log Message:
-----------
Prevent a race condition when creating direct shares.

Modified Paths:
--------------
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/common/datastore/sql_legacy.py

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2011-03-11 19:38:43 UTC (rev 7184)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2011-03-11 21:10:33 UTC (rev 7185)
@@ -41,6 +41,7 @@
 from twistedcaldav import memcacher, caldavxml
 from twistedcaldav.config import config
 from twistedcaldav.dateops import datetimeMktime
+from twistedcaldav.sharing import SharedCollectionRecord
 
 import datetime
 
@@ -573,3 +574,53 @@
         rows = yield _allWithID.on(self.transactionUnderTest(), resourceID=resourceID)
         self.assertEqual(len(tuple(rows)), 0)
         yield self.commit()
+
+    @inlineCallbacks
+    def test_directShareCreateConcurrency(self):
+        """
+        Test that two concurrent attempts to create a direct shared calendar
+        work concurrently without an exception.
+        """
+
+        calendarStore = self._sqlCalendarStore
+
+        # Provision the home and calendar now
+        txn = calendarStore.newTransaction()
+        home = yield txn.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+        self.assertNotEqual(home, None)
+        cal = yield home.calendarWithName("calendar")
+        self.assertNotEqual(cal, None)
+        yield txn.commit()
+
+        txn1 = calendarStore.newTransaction()
+        txn2 = calendarStore.newTransaction()
+
+        home1 = yield txn1.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+        home2 = yield txn2.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+
+        shares1 = yield home1.retrieveOldShares()
+        shares2 = yield home2.retrieveOldShares()
+
+        record = SharedCollectionRecord(
+            "abcd",
+            "D",
+            "/calendars/__uids__/uid2/calendar/",
+            "XYZ",
+            "Shared Wiki Calendar",
+        )
+
+        @inlineCallbacks
+        def _defer1():
+            yield shares1.addOrUpdateRecord(record)
+            yield txn1.commit()
+        d1 = _defer1()
+
+        @inlineCallbacks
+        def _defer2():
+            yield shares2.addOrUpdateRecord(record)
+            yield txn2.commit()
+        d2 = _defer2()
+
+        yield d1
+        yield d2
+

Modified: CalendarServer/trunk/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2011-03-11 19:38:43 UTC (rev 7184)
+++ CalendarServer/trunk/txdav/common/datastore/sql_legacy.py	2011-03-11 21:10:33 UTC (rev 7185)
@@ -44,7 +44,7 @@
 from txdav.common.icommondatastore import (
     IndexedSearchException, ReservationError)
 
-from twext.enterprise.dal.syntax import Update
+from twext.enterprise.dal.syntax import Update, SavepointAction
 from twext.enterprise.dal.syntax import Insert
 from twext.enterprise.dal.syntax import Select
 from twext.enterprise.dal.syntax import Delete
@@ -621,15 +621,27 @@
                 homeID=self._home._resourceID, resourceID=collectionResourceID
             )
         elif record.sharetype == 'D':
-            # There is no bind entry already so add one.
-            yield self._acceptDirectShareQuery.on(
-                self._txn, homeID=self._home._resourceID,
-                resourceID=collectionResourceID, name=record.localname,
-                message=record.summary
-            )
+            # There is no bind entry already so add one - but be aware of possible race to create
 
-        shareeCollection = yield self._home.sharedChildWithName(
-            record.localname)
+            # Use savepoint so we can do a partial rollback if there is a race condition
+            # where this row has already been inserted
+            savepoint = SavepointAction("addOrUpdateRecord")
+            yield savepoint.acquire(self._txn)
+
+            try:
+                yield self._acceptDirectShareQuery.on(
+                    self._txn, homeID=self._home._resourceID,
+                    resourceID=collectionResourceID, name=record.localname,
+                    message=record.summary
+                )
+            except Exception: # FIXME: Really want to trap the pg.DatabaseError but in a non-DB specific manner
+                yield savepoint.rollback(self._txn)
+
+                # For now we will assume that the insert already done is the winner - so nothing more to do here
+            else:
+                yield savepoint.release(self._txn)
+
+        shareeCollection = yield self._home.sharedChildWithName(record.localname)
         yield shareeCollection._initSyncToken()
 
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110311/ee637d27/attachment.html>


More information about the calendarserver-changes mailing list