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

source_changes at macosforge.org source_changes at macosforge.org
Thu Jul 31 14:05:16 PDT 2014


Revision: 13810
          http://trac.calendarserver.org//changeset/13810
Author:   gaya at apple.com
Date:     2014-07-31 14:05:16 -0700 (Thu, 31 Jul 2014)
Log Message:
-----------
delete unused cached groups

Modified Paths:
--------------
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/who/groups.py
    CalendarServer/trunk/txdav/who/test/test_groups.py

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2014-07-31 20:15:15 UTC (rev 13809)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2014-07-31 21:05:16 UTC (rev 13810)
@@ -1107,8 +1107,8 @@
         )
 
         record = yield self.directoryService().recordWithUID(groupUID)
-        yield self._refreshGroup(
-            groupUID, record, groupID, name.encode("utf-8"), membershipHash
+        yield self.refreshGroup(
+            groupUID, record, groupID, name.encode("utf-8"), membershipHash, True
         )
         returnValue(groupID)
 
@@ -1320,45 +1320,8 @@
 
 
     @inlineCallbacks
-    def refreshGroup(self, groupUID):
+    def refreshGroup(self, groupUID, record, groupID, cachedName, cachedMembershipHash, cachedExtant):
         """
-        Refreshes the group membership cache.
-
-        @param groupUID: the group UID
-        @type groupUID: C{unicode}
-
-        @return: Deferred firing with tuple of group ID C{str}, and
-            membershipChanged C{boolean}
-
-        """
-        log.debug("Faulting in group: {g}", g=groupUID)
-        record = (yield self.directoryService().recordWithUID(groupUID))
-        if record is None:
-            # the group has disappeared from the directory
-            log.info("Group is missing: {g}", g=groupUID)
-        else:
-            log.debug("Got group record: {u}", u=record.uid)
-
-        (
-            groupID, cachedName, cachedMembershipHash, _ignore_modified,
-            _ignore_extant
-        ) = yield self.groupByUID(
-            groupUID,
-            create=(record is not None)
-        )
-
-        membershipChanged = False
-        if groupID:
-            membershipChanged = yield self._refreshGroup(
-                groupUID, record, groupID, cachedName, cachedMembershipHash
-            )
-
-        returnValue((groupID, membershipChanged))
-
-
-    @inlineCallbacks
-    def _refreshGroup(self, groupUID, record, groupID, cachedName, cachedMembershipHash):
-        """
         @param groupUID: the directory record
         @type groupUID: C{unicode}
         @param record: the directory record
@@ -1369,6 +1332,8 @@
         @type cachedName: C{unicode}
         @param cachedMembershipHash: membership hash in the database
         @type cachedMembershipHash: C{str}
+        @param cachedExtant: extent field from in the database
+        @type cachedExtant: C{bool}
 
         @return: Deferred firing with membershipChanged C{boolean}
 
@@ -1383,9 +1348,7 @@
             extant = False
 
         membershipHashContent = hashlib.md5()
-        members = list(members)
-        members.sort(key=lambda x: x.uid)
-        for member in members:
+        for member in sorted(members, key=lambda x: x.uid):
             membershipHashContent.update(str(member.uid))
         membershipHash = membershipHashContent.hexdigest()
 
@@ -1397,7 +1360,7 @@
         else:
             membershipChanged = False
 
-        if membershipChanged or record is not None:
+        if membershipChanged or extant != cachedExtant:
             # also updates group mod date
             yield self.updateGroup(
                 groupUID, name, membershipHash, extant=extant

Modified: CalendarServer/trunk/txdav/who/groups.py
===================================================================
--- CalendarServer/trunk/txdav/who/groups.py	2014-07-31 20:15:15 UTC (rev 13809)
+++ CalendarServer/trunk/txdav/who/groups.py	2014-07-31 21:05:16 UTC (rev 13810)
@@ -22,7 +22,7 @@
 from pycalendar.datetime import DateTime
 from pycalendar.duration import Duration
 from twext.enterprise.dal.record import fromTable
-from twext.enterprise.dal.syntax import Delete, Select
+from twext.enterprise.dal.syntax import Delete, Select, Parameter
 from twext.enterprise.jobqueue import WorkItem, RegeneratingWorkItem
 from twext.python.log import Logger
 from twisted.internet.defer import inlineCallbacks, returnValue, succeed
@@ -331,7 +331,16 @@
             )
             self.log.debug("Enqueued group refresh for {u}", u=groupUID)
 
+        # remove unused groups
+        gr = schema.GROUPS
+        yield Delete(
+            From=gr,
+            Where=gr.GROUP_UID.NotIn(
+                Parameter("groupUIDs", len(groupUIDs))
+            ) if groupUIDs else None
+        ).on(txn, groupUIDs=groupUIDs)
 
+
     @inlineCallbacks
     def applyExternalAssignments(self, txn, newAssignments):
 
@@ -380,13 +389,33 @@
             and updates the GROUP_MEMBERSHIP table
             WorkProposal is returned for tests
         """
-        groupID, membershipChanged = yield txn.refreshGroup(groupUID)
+        self.log.debug("Faulting in group: {g}", g=groupUID)
+        record = (yield self.directory.recordWithUID(groupUID))
+        if record is None:
+            # the group has disappeared from the directory
+            self.log.info("Group is missing: {g}", g=groupUID)
+        else:
+            self.log.debug("Got group record: {u}", u=record.uid)
 
-        if membershipChanged:
-            wpsAttendee = yield self.scheduleGroupAttendeeReconciliations(txn, groupID)
-            wpsShareee = yield self.scheduleGroupShareeReconciliations(txn, groupID)
-            returnValue(wpsAttendee + wpsShareee)
+        (
+            groupID, cachedName, cachedMembershipHash, _ignore_modified,
+            cachedExtant
+        ) = yield txn.groupByUID(
+            groupUID,
+            create=(record is not None)
+        )
 
+        if groupID:
+            membershipChanged = yield txn.refreshGroup(
+                groupUID, record, groupID,
+                cachedName, cachedMembershipHash, cachedExtant
+            )
+
+            if membershipChanged:
+                wpsAttendee = yield self.scheduleGroupAttendeeReconciliations(txn, groupID)
+                wpsShareee = yield self.scheduleGroupShareeReconciliations(txn, groupID)
+                returnValue(wpsAttendee + wpsShareee)
+
         returnValue(tuple())
 
 
@@ -462,7 +491,7 @@
 
     @inlineCallbacks
     def groupsToRefresh(self, txn):
-        delegatedUIDs = set((yield txn.allGroupDelegates()))
+        delegatedUIDs = frozenset((yield txn.allGroupDelegates()))
         self.log.info(
             "There are {count} group delegates", count=len(delegatedUIDs)
         )
@@ -481,7 +510,7 @@
                 )
             )
         ).on(txn)
-        attendeeGroupUIDs = set([row[0] for row in rows])
+        attendeeGroupUIDs = frozenset([row[0] for row in rows])
         self.log.info(
             "There are {count} group attendees", count=len(attendeeGroupUIDs)
         )
@@ -500,11 +529,9 @@
                 )
             )
         ).on(txn)
-        shareeGroupUIDs = set([row[0] for row in rows])
+        shareeGroupUIDs = frozenset([row[0] for row in rows])
         self.log.info(
             "There are {count} group sharees", count=len(shareeGroupUIDs)
         )
 
-        # FIXME: is this a good place to clear out unreferenced groups?
-
         returnValue(frozenset(delegatedUIDs | attendeeGroupUIDs | shareeGroupUIDs))

Modified: CalendarServer/trunk/txdav/who/test/test_groups.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_groups.py	2014-07-31 20:15:15 UTC (rev 13809)
+++ CalendarServer/trunk/txdav/who/test/test_groups.py	2014-07-31 21:05:16 UTC (rev 13810)
@@ -478,19 +478,21 @@
                 )
             )
 
-        # Add a group
-        records.append(
-            TestRecord(
-                self.directory,
-                {
-                    fieldName.uid: u"testgroup",
-                    fieldName.recordType: RecordType.group,
-                }
+        # Add two groups
+        for uid in (u"testgroup", u"emptygroup",):
+            records.append(
+                TestRecord(
+                    self.directory,
+                    {
+                        fieldName.uid: uid,
+                        fieldName.recordType: RecordType.group,
+                    }
+                )
             )
-        )
 
-        yield self.directory.updateRecords(records, create=True)
+            yield self.directory.updateRecords(records, create=True)
 
+        # add members to test group
         group = yield self.directory.recordWithUID(u"testgroup")
         members = yield self.directory.recordsWithRecordType(RecordType.user)
         yield group.setMembers(members)
@@ -505,67 +507,129 @@
         """
         store = self.storeUnderTest()
 
-        txn = store.newTransaction()
-        yield self.groupCacher.refreshGroup(txn, u"testgroup")
-        (
-            _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
-            extant
-        ) = (yield txn.groupByUID(u"testgroup"))
-        yield txn.commit()
+        for uid in (u"testgroup", u"emptygroup",):
 
-        self.assertTrue(extant)
+            txn = store.newTransaction()
+            yield self.groupCacher.refreshGroup(txn, uid)
+            (
+                _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+                extant
+            ) = (yield txn.groupByUID(uid))
+            yield txn.commit()
 
-        # Remove the group
-        yield self.directory.removeRecords([u"testgroup"])
+            self.assertTrue(extant)
 
-        txn = store.newTransaction()
-        yield self.groupCacher.refreshGroup(txn, u"testgroup")
-        (
-            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
-            extant
-        ) = (yield txn.groupByUID(u"testgroup"))
-        yield txn.commit()
+            # Remove the group
+            yield self.directory.removeRecords([uid])
 
-        # Extant = False
-        self.assertFalse(extant)
+            txn = store.newTransaction()
+            yield self.groupCacher.refreshGroup(txn, uid)
+            (
+                groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+                extant
+            ) = (yield txn.groupByUID(uid))
+            yield txn.commit()
 
-        # The list of members stored in the DB for this group is now empty
-        txn = store.newTransaction()
-        members = yield txn.groupMemberUIDs(groupID)
-        yield txn.commit()
-        self.assertEquals(members, set())
+            # Extant = False
+            self.assertFalse(extant)
 
-        # Add the group back into the directory
-        fieldName = self.directory.fieldName
-        yield self.directory.updateRecords(
-            (
-                TestRecord(
-                    self.directory,
-                    {
-                        fieldName.uid: u"testgroup",
-                        fieldName.recordType: RecordType.group,
-                    }
+            # The list of members stored in the DB for this group is now empty
+            txn = store.newTransaction()
+            members = yield txn.groupMemberUIDs(groupID)
+            yield txn.commit()
+            self.assertEquals(members, set())
+
+            # Add the group back into the directory
+            fieldName = self.directory.fieldName
+            yield self.directory.updateRecords(
+                (
+                    TestRecord(
+                        self.directory,
+                        {
+                            fieldName.uid: uid,
+                            fieldName.recordType: RecordType.group,
+                        }
+                    ),
                 ),
-            ),
-            create=True
-        )
-        group = yield self.directory.recordWithUID(u"testgroup")
-        members = yield self.directory.recordsWithRecordType(RecordType.user)
-        yield group.setMembers(members)
+                create=True
+            )
+            if uid == u"testgroup":
+                group = yield self.directory.recordWithUID(uid)
+                members = yield self.directory.recordsWithRecordType(RecordType.user)
+                yield group.setMembers(members)
 
+            txn = store.newTransaction()
+            yield self.groupCacher.refreshGroup(txn, uid)
+            (
+                groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+                extant
+            ) = (yield txn.groupByUID(uid))
+            yield txn.commit()
+
+            # Extant = True
+            self.assertTrue(extant)
+
+            # The list of members stored in the DB for this group has 100 users
+            txn = store.newTransaction()
+            members = yield txn.groupMemberUIDs(groupID)
+            yield txn.commit()
+            self.assertEquals(len(members), 100 if uid == u"testgroup" else 0)
+
+
+    @inlineCallbacks
+    def test_update_delete(self):
+        """
+        Verify that unused groups are deleted from group cache
+        """
+        store = self.storeUnderTest()
+
+        # unused group deleted
+        for uid in (u"testgroup", u"emptygroup",):
+
+            txn = store.newTransaction()
+            yield self.groupCacher.refreshGroup(txn, uid)
+            groupID = (yield txn.groupByUID(uid, create=False))[0]
+            yield txn.commit()
+
+            self.assertNotEqual(groupID, None)
+
+            txn = store.newTransaction()
+            yield self.groupCacher.update(txn)
+            groupID = (yield txn.groupByUID(uid, create=False))[0]
+
+            self.assertEqual(groupID, None)
+
+        # delegate groups not deleted
+        for uid in (u"testgroup", u"emptygroup",):
+
+            groupID = (yield txn.groupByUID(uid))[0]
+            yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=groupID, readWrite=True)
+            yield txn.commit()
+
+            self.assertNotEqual(groupID, None)
+
+            txn = store.newTransaction()
+            yield self.groupCacher.update(txn)
+            groupID = (yield txn.groupByUID(uid, create=False))[0]
+
+            self.assertNotEqual(groupID, None)
+
+        # delegate group is deleted. unused group is deleted
         txn = store.newTransaction()
-        yield self.groupCacher.refreshGroup(txn, u"testgroup")
-        (
-            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
-            extant
-        ) = (yield txn.groupByUID(u"testgroup"))
+        testGroupID = (yield txn.groupByUID(u"testgroup", create=False))[0]
+        yield txn.removeDelegateGroup(delegator=u"sagen", delegateGroupID=testGroupID, readWrite=True)
+        testGroupID = (yield txn.groupByUID(u"testgroup", create=False))[0]
+        emptyGroupID = (yield txn.groupByUID(u"emptygroup", create=False))[0]
         yield txn.commit()
 
-        # Extant = True
-        self.assertTrue(extant)
+        self.assertNotEqual(testGroupID, None)
+        self.assertNotEqual(emptyGroupID, None)
 
-        # The list of members stored in the DB for this group has 100 users
         txn = store.newTransaction()
-        members = yield txn.groupMemberUIDs(groupID)
+        yield self.groupCacher.update(txn)
+        testGroupID = (yield txn.groupByUID(u"testgroup", create=False))[0]
+        emptyGroupID = (yield txn.groupByUID(u"emptygroup", create=False))[0]
         yield txn.commit()
-        self.assertEquals(len(members), 100)
+
+        self.assertEqual(testGroupID, None)
+        self.assertNotEqual(emptyGroupID, None)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140731/86e01c8e/attachment-0001.html>


More information about the calendarserver-changes mailing list