[CalendarServer-changes] [6200] CalendarServer/trunk/txdav
source_changes at macosforge.org
source_changes at macosforge.org
Fri Aug 27 19:53:30 PDT 2010
Revision: 6200
http://trac.macosforge.org/projects/calendarserver/changeset/6200
Author: cdaboo at apple.com
Date: 2010-08-27 19:53:27 -0700 (Fri, 27 Aug 2010)
Log Message:
-----------
Implement locking for concurrent home provisioning issues.
Modified Paths:
--------------
CalendarServer/trunk/txdav/base/datastore/subpostgres.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py
CalendarServer/trunk/txdav/common/datastore/sql.py
Modified: CalendarServer/trunk/txdav/base/datastore/subpostgres.py
===================================================================
--- CalendarServer/trunk/txdav/base/datastore/subpostgres.py 2010-08-27 17:45:52 UTC (rev 6199)
+++ CalendarServer/trunk/txdav/base/datastore/subpostgres.py 2010-08-28 02:53:27 UTC (rev 6200)
@@ -255,7 +255,7 @@
# See: http://www.postgresql.org/docs/8.4/static/kernel-resources.html
if testMode:
self.sharedBuffers = 16
- self.maxConnections = 2
+ self.maxConnections = 4
else:
self.sharedBuffers = 30
self.maxConnections = 20
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2010-08-27 17:45:52 UTC (rev 6199)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py 2010-08-28 02:53:27 UTC (rev 6200)
@@ -19,13 +19,17 @@
L{txdav.caldav.datastore.test.common}.
"""
+import time
+
from txdav.caldav.datastore.test.common import CommonTests as CalendarCommonTests
+from txdav.common.datastore.sql import ECALENDARTYPE
from txdav.common.datastore.test.util import SQLStoreBuilder
from txdav.common.icommondatastore import NoSuchHomeChildError
from twisted.trial import unittest
from twisted.internet.defer import inlineCallbacks
+from twisted.internet.threads import deferToThread
from twext.python.vcomponent import VComponent
@@ -76,3 +80,46 @@
Create and return a L{CalendarStore} for testing.
"""
return self.calendarStore
+
+ @inlineCallbacks
+ def test_homeProvisioningConcurrency(self):
+
+ calendarStore1 = yield buildStore(self, self.notifierFactory)
+ calendarStore2 = yield buildStore(self, self.notifierFactory)
+ calendarStore3 = yield buildStore(self, self.notifierFactory)
+
+ txn1 = calendarStore1.newTransaction()
+ txn2 = calendarStore2.newTransaction()
+ txn3 = calendarStore3.newTransaction()
+
+ # Provision one home now
+ home_uid2 = txn3.homeWithUID(ECALENDARTYPE, "uid2", create=True)
+ self.assertNotEqual(home_uid2, None)
+ txn3.commit()
+
+ home_uid1_1 = txn1.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+
+ def _defer_home_uid1_2():
+ home_uid1_2 = txn2.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+ txn2.commit()
+ return home_uid1_2
+ d1 = deferToThread(_defer_home_uid1_2)
+
+ def _pause_home_uid1_1():
+ time.sleep(1)
+ txn1.commit()
+ d2 = deferToThread(_pause_home_uid1_1)
+
+ # Verify that we can still get to the existing home - i.e. the lock
+ # on the table allows concurrent reads
+ txn4 = calendarStore3.newTransaction()
+ home_uid2 = txn4.homeWithUID(ECALENDARTYPE, "uid2", create=True)
+ self.assertNotEqual(home_uid2, None)
+ txn4.commit()
+
+ # Now do the concurrent provision attempt
+ yield d2
+ home_uid1_2 = yield d1
+
+ self.assertNotEqual(home_uid1_1, None)
+ self.assertNotEqual(home_uid1_2, None)
Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py 2010-08-27 17:45:52 UTC (rev 6199)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py 2010-08-28 02:53:27 UTC (rev 6200)
@@ -19,15 +19,18 @@
L{txdav.caldav.datastore.test.common}.
"""
+import time
+
from txdav.carddav.datastore.test.common import CommonTests as AddressBookCommonTests
+from txdav.common.datastore.sql import EADDRESSBOOKTYPE
from txdav.common.datastore.test.util import SQLStoreBuilder
from twisted.trial import unittest
from twisted.internet.defer import inlineCallbacks
+from twisted.internet.threads import deferToThread
from twistedcaldav.vcard import Component as VCard
-
theStoreBuilder = SQLStoreBuilder()
buildStore = theStoreBuilder.buildStore
@@ -73,3 +76,46 @@
"""
return self.addressbookStore
+
+ @inlineCallbacks
+ def test_homeProvisioningConcurrency(self):
+
+ addressbookStore1 = yield buildStore(self, self.notifierFactory)
+ addressbookStore2 = yield buildStore(self, self.notifierFactory)
+ addressbookStore3 = yield buildStore(self, self.notifierFactory)
+
+ txn1 = addressbookStore1.newTransaction()
+ txn2 = addressbookStore2.newTransaction()
+ txn3 = addressbookStore3.newTransaction()
+
+ # Provision one home now
+ home_uid2 = txn3.homeWithUID(EADDRESSBOOKTYPE, "uid2", create=True)
+ self.assertNotEqual(home_uid2, None)
+ txn3.commit()
+
+ home_uid1_1 = txn1.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
+
+ def _defer_home_uid1_2():
+ home_uid1_2 = txn2.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
+ txn2.commit()
+ return home_uid1_2
+ d1 = deferToThread(_defer_home_uid1_2)
+
+ def _pause_home_uid1_1():
+ time.sleep(1)
+ txn1.commit()
+ d2 = deferToThread(_pause_home_uid1_1)
+
+ # Verify that we can still get to the existing home - i.e. the lock
+ # on the table allows concurrent reads
+ txn4 = addressbookStore3.newTransaction()
+ home_uid2 = txn4.homeWithUID(EADDRESSBOOKTYPE, "uid2", create=True)
+ self.assertNotEqual(home_uid2, None)
+ txn4.commit()
+
+ # Now do the concurrent provision attempt
+ yield d2
+ home_uid1_2 = yield d1
+
+ self.assertNotEqual(home_uid1_1, None)
+ self.assertNotEqual(home_uid1_2, None)
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2010-08-27 17:45:52 UTC (rev 6199)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2010-08-28 02:53:27 UTC (rev 6200)
@@ -169,13 +169,30 @@
if not data:
if not create:
return None
+
+ # Need to lock to prevent race condition
+ # FIXME: this is an entire table lock - ideally we want a row lock
+ # but the row does not exist yet. However, the "exclusive" mode does
+ # allow concurrent reads so the only thing we block is other attempts
+ # to provision a home, which is not too bad
self.execSQL(
- "insert into %(name)s (%(column_OWNER_UID)s) values (%%s)" % homeTable,
+ "lock %(name)s in exclusive mode" % homeTable,
+ )
+
+ # Now test again
+ data = self.execSQL(
+ "select %(column_RESOURCE_ID)s from %(name)s where %(column_OWNER_UID)s = %%s" % homeTable,
[uid]
)
- home = self.homeWithUID(storeType, uid)
- home.createdHome()
- return home
+
+ if not data:
+ self.execSQL(
+ "insert into %(name)s (%(column_OWNER_UID)s) values (%%s)" % homeTable,
+ [uid]
+ )
+ home = self.homeWithUID(storeType, uid)
+ home.createdHome()
+ return home
resid = data[0][0]
if self._notifierFactory:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100827/531ff0d0/attachment-0001.html>
More information about the calendarserver-changes
mailing list