Skip to content
Permalink
Browse files

Fix issue with deadlock on common home child revisions table change.

  • Loading branch information...
cyrusdaboo committed Jun 25, 2013
1 parent b4be47c commit e6593f48bbd807e28eaa158007e7e4c43cdc8fdf
@@ -15,9 +15,6 @@
# limitations under the License.
##

from twext.enterprise.locking import NamedLock
from urlparse import urlparse, urlunparse
from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler

"""
SQL backend for CalDAV storage.
@@ -36,9 +33,8 @@
from twext.enterprise.dal.syntax import Select, Count, ColumnSyntax
from twext.enterprise.dal.syntax import Update
from twext.enterprise.dal.syntax import utcNowSQL

from twext.enterprise.locking import NamedLock
from twext.enterprise.util import parseSQLTimestamp

from twext.python.clsprop import classproperty
from twext.python.filepath import CachingFilePath
from twext.python.log import Logger
@@ -49,6 +45,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.python import hashlib


from twistedcaldav import caldavxml, customxml
from twistedcaldav.config import config
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
@@ -57,8 +54,8 @@
from twistedcaldav.ical import Component, InvalidICalendarDataError, Property
from twistedcaldav.instance import InvalidOverriddenInstanceError
from twistedcaldav.memcacher import Memcacher

from txdav.base.propertystore.base import PropertyName
from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
from txdav.caldav.datastore.util import AttachmentRetrievalTransport, \
normalizationLookup
from txdav.caldav.datastore.util import CalendarObjectBase
@@ -96,6 +93,7 @@

from zope.interface.declarations import implements

from urlparse import urlparse, urlunparse
import collections
import os
import tempfile
@@ -706,7 +704,7 @@ def setDefaultCalendar(self, calendar, tasks=False):
# CalDAV stores the default calendar properties on the inbox so we also need to send a changed notification on that
inbox = (yield self.calendarWithName("inbox"))
if inbox is not None:
yield inbox.notifyChanged()
yield inbox.notifyPropertyChanged()


@inlineCallbacks
@@ -859,6 +857,11 @@ def setAvailability(self, availability):
yield self.invalidateQueryCache()
yield self.notifyChanged()

# CalDAV stores the availability properties on the inbox so we also need to send a changed notification on that
inbox = (yield self.calendarWithName("inbox"))
if inbox is not None:
yield inbox.notifyPropertyChanged()


CalendarHome._register(ECALENDARTYPE)

@@ -1040,7 +1043,7 @@ def setSupportedComponents(self, supported_components):
).on(self._txn)
self._supportedComponents = supported_components
yield self.invalidateQueryCache()
yield self.notifyChanged()
yield self.notifyPropertyChanged()


def getTimezone(self):
@@ -1073,7 +1076,7 @@ def setTimezone(self, timezone):
Where=(cal.CALENDAR_HOME_RESOURCE_ID == self.viewerHome()._resourceID).And(cal.CALENDAR_RESOURCE_ID == self._resourceID)
).on(self._txn)
yield self.invalidateQueryCache()
yield self.notifyChanged()
yield self.notifyPropertyChanged()

ALARM_DETAILS = {
(True, True): (_bindSchema.ALARM_VEVENT_TIMED, "_alarm_vevent_timed"),
@@ -1120,7 +1123,7 @@ def setDefaultAlarm(self, alarm, vevent, timed):
Where=(cal.CALENDAR_HOME_RESOURCE_ID == self.viewerHome()._resourceID).And(cal.CALENDAR_RESOURCE_ID == self._resourceID)
).on(self._txn)
yield self.invalidateQueryCache()
yield self.notifyChanged()
yield self.notifyPropertyChanged()


def isInbox(self):
@@ -1160,7 +1163,7 @@ def setUsedForFreeBusy(self, use_it):
Where=(cal.CALENDAR_HOME_RESOURCE_ID == self.viewerHome()._resourceID).And(cal.CALENDAR_RESOURCE_ID == self._resourceID)
).on(self._txn)
yield self.invalidateQueryCache()
yield self.notifyChanged()
yield self.notifyPropertyChanged()


def initPropertyStore(self, props):
@@ -26,7 +26,7 @@
from twext.python.vcomponent import VComponent

from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.defer import inlineCallbacks, returnValue, DeferredList
from twisted.internet.task import deferLater
from twisted.trial import unittest

@@ -607,7 +607,7 @@ def _defer1():
yield cal1.createObjectResourceWithName("1.ics", VComponent.fromString(
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.1//EN
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:US/Pacific
@@ -653,7 +653,7 @@ def _defer2():
yield cal2.createObjectResourceWithName("2.ics", VComponent.fromString(
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//iCal 4.0.1//EN
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:US/Pacific
@@ -1557,7 +1557,7 @@ def _createInboxItem(rname, pvalue):
@inlineCallbacks
def test_objectResourceWithID(self):
"""
L{ICalendarHome.objectResourceWithID} will return the calendar object..
L{ICalendarHome.objectResourceWithID} will return the calendar object.
"""
home = yield self.homeUnderTest()
calendarObject = (yield home.objectResourceWithID(9999))
@@ -1571,7 +1571,7 @@ def test_objectResourceWithID(self):
@inlineCallbacks
def test_defaultAlarms(self):
"""
L{ICalendarHome.objectResourceWithID} will return the calendar object..
L{ICalendarHome.objectResourceWithID} will return the calendar object.
"""

alarmhome1 = """BEGIN:VALARM
@@ -1859,3 +1859,80 @@ def test_setTimezone(self):
cal = yield self.calendarUnderTest()
self.assertEqual(cal.getTimezone(), None)
yield self.commit()


@inlineCallbacks
def test_calendarRevisionChangeConcurrency(self):
"""
Test that two concurrent attempts to add resources in two separate
calendar homes does not deadlock on the revision table update.
"""

calendarStore = self._sqlCalendarStore

# Make sure homes are provisioned
txn = self.transactionUnderTest()
home_uid1 = yield txn.homeWithUID(ECALENDARTYPE, "user01", create=True)
home_uid2 = yield txn.homeWithUID(ECALENDARTYPE, "user02", create=True)
self.assertNotEqual(home_uid1, None)
self.assertNotEqual(home_uid2, None)
yield self.commit()

# Create first events in different calendar homes
txn1 = calendarStore.newTransaction()
txn2 = calendarStore.newTransaction()

calendar_uid1_in_txn1 = yield self.calendarUnderTest(txn1, "calendar", "user01")
calendar_uid2_in_txn2 = yield self.calendarUnderTest(txn2, "calendar", "user02")

data = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
BEGIN:VEVENT
UID:data%(ctr)s
DTSTART:20130102T140000Z
DURATION:PT1H
CREATED:20060102T190000Z
DTSTAMP:20051222T210507Z
SUMMARY:data%(ctr)s
END:VEVENT
END:VCALENDAR
"""

component = Component.fromString(data % {"ctr": 1})
yield calendar_uid1_in_txn1.createCalendarObjectWithName("data1.ics", component)

component = Component.fromString(data % {"ctr": 2})
yield calendar_uid2_in_txn2.createCalendarObjectWithName("data2.ics", component)

# Setup deferreds to run concurrently and create second events in the calendar homes
# previously used by the other transaction - this could create the deadlock.
@inlineCallbacks
def _defer_uid3():
calendar_uid1_in_txn2 = yield self.calendarUnderTest(txn2, "calendar", "user01")
component = Component.fromString(data % {"ctr": 3})
yield calendar_uid1_in_txn2.createCalendarObjectWithName("data3.ics", component)
yield txn2.commit()
d1 = _defer_uid3()

@inlineCallbacks
def _defer_uid4():
calendar_uid2_in_txn1 = yield self.calendarUnderTest(txn1, "calendar", "user02")
component = Component.fromString(data % {"ctr": 4})
yield calendar_uid2_in_txn1.createCalendarObjectWithName("data4.ics", component)
yield txn1.commit()
d2 = _defer_uid4()

# Now do the concurrent provision attempt
yield DeferredList([d1, d2])

# Verify we did not have a deadlock and all resources have been created.
caldata1 = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="user01")
caldata2 = yield self.calendarObjectUnderTest(name="data2.ics", calendar_name="calendar", home="user02")
caldata3 = yield self.calendarObjectUnderTest(name="data3.ics", calendar_name="calendar", home="user01")
caldata4 = yield self.calendarObjectUnderTest(name="data4.ics", calendar_name="calendar", home="user02")
self.assertNotEqual(caldata1, None)
self.assertNotEqual(caldata2, None)
self.assertNotEqual(caldata3, None)
self.assertNotEqual(caldata4, None)

0 comments on commit e6593f4

Please sign in to comment.
You can’t perform that action at this time.