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

source_changes at macosforge.org source_changes at macosforge.org
Wed Jul 10 19:43:00 PDT 2013


Revision: 11503
          http://trac.calendarserver.org//changeset/11503
Author:   cdaboo at apple.com
Date:     2013-07-10 19:43:00 -0700 (Wed, 10 Jul 2013)
Log Message:
-----------
Fix concurrency issue when copying over timezone data.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/test/test_timezones.py
    CalendarServer/trunk/twistedcaldav/timezones.py

Modified: CalendarServer/trunk/twistedcaldav/test/test_timezones.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_timezones.py	2013-07-10 21:03:01 UTC (rev 11502)
+++ CalendarServer/trunk/twistedcaldav/test/test_timezones.py	2013-07-11 02:43:00 UTC (rev 11503)
@@ -23,6 +23,7 @@
 from pycalendar.timezone import PyCalendarTimezone
 
 import os
+import threading
 
 class TimezoneProblemTest (twistedcaldav.test.util.TestCase):
     """
@@ -216,6 +217,7 @@
     """
 
     def setUp(self):
+        super(TimezonePackageTest, self).setUp()
         TimezoneCache.clear()
         TimezoneCache.create()
 
@@ -274,3 +276,32 @@
         copy_tz = readTZ("America/New_York")
 
         self.assertEqual(str(pkg_tz), str(copy_tz))
+
+
+    def test_copyPackage_Concurrency(self):
+        """
+        Test that concurrent copying of the tz package works.
+        """
+
+        self.patch(config, "UsePackageTimezones", False)
+        TimezoneCache.clear()
+
+        ex = [False, False]
+        def _try(n):
+            try:
+                TimezoneCache.create()
+            except:
+                ex[n] = True
+
+        t1 = threading.Thread(target=_try, args=(0,))
+        t2 = threading.Thread(target=_try, args=(1,))
+        t1.start()
+        t2.start()
+        t1.join()
+        t2.join()
+
+        self.assertFalse(ex[0])
+        self.assertFalse(ex[1])
+
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, "zoneinfo")))
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, "zoneinfo", "America", "New_York.ics")))

Modified: CalendarServer/trunk/twistedcaldav/timezones.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/timezones.py	2013-07-10 21:03:01 UTC (rev 11502)
+++ CalendarServer/trunk/twistedcaldav/timezones.py	2013-07-11 02:43:00 UTC (rev 11503)
@@ -128,24 +128,48 @@
     def validatePath():
         dbpath = FilePath(TimezoneCache.getDBPath())
         if not dbpath.exists():
-            # Move package data to the path
-            pkgpath = TimezoneCache.FilteredFilePath(TimezoneCache._getPackageDBPath())
-            log.info("Copying timezones from %s to %s" % (pkgpath.path, dbpath.path,))
-            pkgpath.copyFilteredDirectoryTo(dbpath)
+            TimezoneCache.copyPackage("Copying")
         else:
             # Check if pkg is more recent and copy over
             pkgversion = TimezoneCache.getTZVersion(TimezoneCache._getPackageDBPath())
             dbversion = TimezoneCache.getTZVersion(dbpath.path)
             if pkgversion > dbversion:
                 dbpath.remove()
-                pkgpath = TimezoneCache.FilteredFilePath(TimezoneCache._getPackageDBPath())
-                log.info("Updating timezones at %s with %s" % (dbpath.path, pkgpath.path,))
-                pkgpath.copyFilteredDirectoryTo(dbpath)
+                TimezoneCache.copyPackage("Updating")
             else:
                 log.info("Valid timezones at %s" % (dbpath.path,))
 
 
     @staticmethod
+    def copyPackage(title):
+        """
+        Copy package directory to db path using a temporary sibling to avoid potential
+        concurrency race conditions.
+
+        @param title: string to use in log entry
+        @type title: C{str}
+        """
+        dbpath = FilePath(TimezoneCache.getDBPath())
+        pkgpath = TimezoneCache.FilteredFilePath(TimezoneCache._getPackageDBPath())
+        log.info(
+            "{title} timezones from {pkg} to {to}",
+            title=title,
+            pkg=pkgpath.path,
+            to=dbpath.path
+        )
+
+        # Use temp directory to copy to first
+        temp = dbpath.temporarySibling()
+        pkgpath.copyFilteredDirectoryTo(temp)
+
+        # Move to actual path if it stll does not exist
+        if not dbpath.exists():
+            temp.moveTo(dbpath)
+        else:
+            temp.remove()
+
+
+    @staticmethod
     def clear():
         PyCalendarTimezoneDatabase.clearTimezoneDatabase()
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130710/07a880d7/attachment-0001.html>


More information about the calendarserver-changes mailing list