[CalendarServer-changes] [11517] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Jul 15 16:20:39 PDT 2013


Revision: 11517
          http://trac.calendarserver.org//changeset/11517
Author:   sagen at apple.com
Date:     2013-07-15 16:20:38 -0700 (Mon, 15 Jul 2013)
Log Message:
-----------
Don't wait for group cache update to finish before scheduling the next one, add a memcached lock around it, and deferToThread the timedSearch() call from recordsMatchingFields()

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tap/caldav.py
    CalendarServer/trunk/twistedcaldav/directory/directory.py
    CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/twistedcaldav/upgrade.py

Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -870,6 +870,7 @@
                 directory,
                 config.GroupCaching.UpdateSeconds,
                 config.GroupCaching.ExpireSeconds,
+                config.GroupCaching.LockSeconds,
                 namespace=config.GroupCaching.MemcachedPool,
                 useExternalProxies=config.GroupCaching.UseExternalProxies
                 )
@@ -1171,6 +1172,7 @@
                     directory,
                     config.GroupCaching.UpdateSeconds,
                     config.GroupCaching.ExpireSeconds,
+                    config.GroupCaching.LockSeconds,
                     namespace=config.GroupCaching.MemcachedPool,
                     useExternalProxies=config.GroupCaching.UseExternalProxies
                     )
@@ -1638,6 +1640,7 @@
                     directory,
                     config.GroupCaching.UpdateSeconds,
                     config.GroupCaching.ExpireSeconds,
+                    config.GroupCaching.LockSeconds,
                     namespace=config.GroupCaching.MemcachedPool,
                     useExternalProxies=config.GroupCaching.UseExternalProxies
                     )

Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -609,15 +609,15 @@
     log = Logger()
 
     def __init__(self, namespace, pickle=True, no_invalidation=False,
-        key_normalization=True, expireSeconds=0):
+        key_normalization=True, expireSeconds=0, lockSeconds=60):
 
         super(GroupMembershipCache, self).__init__(namespace, pickle=pickle,
             no_invalidation=no_invalidation,
             key_normalization=key_normalization)
 
         self.expireSeconds = expireSeconds
+        self.lockSeconds = lockSeconds
 
-
     def setGroupsFor(self, guid, memberships):
         self.log.debug("set groups-for %s : %s" % (guid, memberships))
         return self.set("groups-for:%s" %
@@ -654,7 +654,36 @@
         returnValue(value is not None)
 
 
+    def acquireLock(self):
+        """
+        Acquire a memcached lock named group-cacher-lock
 
+        return: Deferred firing True if successful, False if someone already has
+            the lock
+        """
+        self.log.debug("add group-cacher-lock")
+        return self.add("group-cacher-lock", "1", expireTime=self.lockSeconds)
+
+
+
+    def extendLock(self):
+        """
+        Update the expiration time of the memcached lock
+        Return: Deferred firing True if successful, False otherwise
+        """
+        self.log.debug("extend group-cacher-lock")
+        return self.set("group-cacher-lock", "1", expireTime=self.lockSeconds)
+
+
+    def releaseLock(self):
+        """
+        Release the memcached lock
+        Return: Deferred firing True if successful, False otherwise
+        """
+        self.log.debug("delete group-cacher-lock")
+        return self.delete("group-cacher-lock")
+
+
 class GroupMembershipCacheUpdater(object):
     """
     Responsible for updating memcached with group memberships.  This will run
@@ -664,7 +693,7 @@
     log = Logger()
 
     def __init__(self, proxyDB, directory, updateSeconds, expireSeconds,
-        cache=None, namespace=None, useExternalProxies=False,
+        lockSeconds, cache=None, namespace=None, useExternalProxies=False,
         externalProxiesSource=None):
         self.proxyDB = proxyDB
         self.directory = directory
@@ -676,7 +705,8 @@
 
         if cache is None:
             assert namespace is not None, "namespace must be specified if GroupMembershipCache is not provided"
-            cache = GroupMembershipCache(namespace, expireSeconds=expireSeconds)
+            cache = GroupMembershipCache(namespace, expireSeconds=expireSeconds,
+                lockSeconds=lockSeconds)
         self.cache = cache
 
 
@@ -764,6 +794,8 @@
 
         # TODO: add memcached eviction protection
 
+        useLock = True
+
         # See if anyone has completely populated the group membership cache
         isPopulated = (yield self.cache.isPopulated())
 
@@ -774,6 +806,9 @@
                 self.log.info("Group membership cache is already populated")
                 returnValue((fast, 0))
 
+            # We don't care what others are doing right now, we need to update
+            useLock = False
+
         self.log.info("Updating group membership cache")
 
         dataRoot = FilePath(config.DataRoot)
@@ -791,6 +826,14 @@
             previousMembers = pickle.loads(membershipsCacheFile.getContent())
             callGroupsChanged = True
 
+        if useLock:
+            self.log.info("Attempting to acquire group membership cache lock")
+            acquiredLock = (yield self.cache.acquireLock())
+            if not acquiredLock:
+                self.log.info("Group membership cache lock held by another process")
+                returnValue((fast, 0))
+            self.log.info("Acquired lock")
+
         if not fast and self.useExternalProxies:
 
             # Load in cached copy of external proxies so we can diff against them
@@ -800,11 +843,17 @@
                     (extProxyCacheFile.path,))
                 previousAssignments = pickle.loads(extProxyCacheFile.getContent())
 
+            if useLock:
+                yield self.cache.extendLock()
+
             self.log.info("Retrieving proxy assignments from directory")
             assignments = self.externalProxiesSource()
             self.log.info("%d proxy assignments retrieved from directory" %
                 (len(assignments),))
 
+            if useLock:
+                yield self.cache.extendLock()
+
             changed, removed = diffAssignments(previousAssignments, assignments)
             # changed is the list of proxy assignments (either new or updates).
             # removed is the list of principals who used to have an external
@@ -960,6 +1009,10 @@
 
         yield self.cache.setPopulatedMarker()
 
+        if useLock:
+            self.log.info("Releasing lock")
+            yield self.cache.releaseLock()
+
         self.log.info("Group memberships cache updated")
 
         returnValue((fast, len(members), len(changedMembers)))
@@ -978,16 +1031,19 @@
 
         groupCacher = getattr(self.transaction, "_groupCacher", None)
         if groupCacher is not None:
+
+            # Schedule next update
+            notBefore = (datetime.datetime.utcnow() +
+                datetime.timedelta(seconds=groupCacher.updateSeconds))
+            log.debug("Scheduling next group cacher update: %s" % (notBefore,))
+            yield self.transaction.enqueue(GroupCacherPollingWork,
+                notBefore=notBefore)
+
             try:
-                yield groupCacher.updateCache()
+                groupCacher.updateCache()
             except Exception, e:
                 log.error("Failed to update group membership cache (%s)" % (e,))
-            finally:
-                notBefore = (datetime.datetime.utcnow() +
-                    datetime.timedelta(seconds=groupCacher.updateSeconds))
-                log.debug("Scheduling next group cacher update: %s" % (notBefore,))
-                yield self.transaction.enqueue(GroupCacherPollingWork,
-                    notBefore=notBefore)
+
         else:
             notBefore = (datetime.datetime.utcnow() +
                 datetime.timedelta(seconds=10))

Modified: CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/twistedcaldav/directory/ldapdirectory.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -58,6 +58,7 @@
 from twistedcaldav.directory.augment import AugmentRecord
 from twistedcaldav.directory.util import splitIntoBatches
 from twisted.internet.defer import succeed, inlineCallbacks, returnValue
+from twisted.internet.threads import deferToThread
 from twext.python.log import Logger
 from twext.web2.http import HTTPError, StatusResponse
 from twext.web2 import responsecode
@@ -1146,6 +1147,7 @@
         return succeed(records)
 
 
+    @inlineCallbacks
     def recordsMatchingFields(self, fields, operand="or", recordType=None):
         """
         Carries out the work of a principal-property-search against LDAP
@@ -1192,10 +1194,10 @@
                 # Query the LDAP server
                 self.log.debug("LDAP search %s %s %s" %
                     (ldap.dn.dn2str(base), scope, filterstr))
-                results = self.timedSearch(ldap.dn.dn2str(base), scope,
+                results = (yield deferToThread(self.timedSearch, ldap.dn.dn2str(base), scope,
                     filterstr=filterstr, attrlist=self.attrlist,
                     timeoutSeconds=self.requestTimeoutSeconds,
-                    resultLimit=self.requestResultsLimit)
+                    resultLimit=self.requestResultsLimit))
                 self.log.debug("LDAP search returned %d results" % (len(results),))
                 numMissingGuids = 0
                 numMissingRecordNames = 0
@@ -1233,7 +1235,7 @@
                         (numMissingRecordNames, recordType))
 
         self.log.debug("Principal property search matched %d records" % (len(records),))
-        return succeed(records)
+        returnValue(records)
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_directory.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -173,7 +173,7 @@
         self.directoryService.groupMembershipCache = cache
 
         updater = GroupMembershipCacheUpdater(
-            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30,
+            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30, 30,
             cache=cache, useExternalProxies=False)
 
         # Exercise getGroups()
@@ -372,7 +372,7 @@
             ]
 
         updater = GroupMembershipCacheUpdater(
-            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30,
+            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30, 30,
             cache=cache, useExternalProxies=True,
             externalProxiesSource=fakeExternalProxies)
 
@@ -456,7 +456,7 @@
             ]
 
         updater = GroupMembershipCacheUpdater(
-            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30,
+            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30, 30,
             cache=cache, useExternalProxies=True,
             externalProxiesSource=fakeExternalProxiesRemoved)
 
@@ -623,7 +623,7 @@
         self.directoryService.groupMembershipCache = cache
 
         updater = GroupMembershipCacheUpdater(
-            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30,
+            calendaruserproxy.ProxyDBService, self.directoryService, 30, 30, 30,
             cache=cache)
 
         dataRoot = FilePath(config.DataRoot)

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_ldapdirectory.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -1593,7 +1593,7 @@
             cache = GroupMembershipCache("ProxyDB", expireSeconds=60)
             self.service.groupMembershipCache = cache
             updater = GroupMembershipCacheUpdater(calendaruserproxy.ProxyDBService,
-                self.service, 30, 15, cache=cache, useExternalProxies=False)
+                self.service, 30, 15, 30, cache=cache, useExternalProxies=False)
 
             self.assertEquals((False, 8, 8), (yield updater.updateCache()))
 

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -952,7 +952,8 @@
         "Enabled": True,
         "MemcachedPool" : "Default",
         "UpdateSeconds" : 300,
-        "ExpireSeconds" : 3600,
+        "ExpireSeconds" : 86400,
+        "LockSeconds"   : 600,
         "EnableUpdater" : True,
         "UseExternalProxies" : False,
     },

Modified: CalendarServer/trunk/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/upgrade.py	2013-07-14 18:02:13 UTC (rev 11516)
+++ CalendarServer/trunk/twistedcaldav/upgrade.py	2013-07-15 23:20:38 UTC (rev 11517)
@@ -1044,6 +1044,7 @@
                     directory,
                     self.config.GroupCaching.UpdateSeconds,
                     self.config.GroupCaching.ExpireSeconds,
+                    self.config.GroupCaching.LockSeconds,
                     namespace=self.config.GroupCaching.MemcachedPool,
                     useExternalProxies=self.config.GroupCaching.UseExternalProxies)
                 yield updater.updateCache(fast=True)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130715/e03b1923/attachment-0001.html>


More information about the calendarserver-changes mailing list