[CalendarServer-changes] [7802] CalendarServer/trunk/contrib/performance/loadtest

source_changes at macosforge.org source_changes at macosforge.org
Fri Jul 15 08:56:51 PDT 2011


Revision: 7802
          http://trac.macosforge.org/projects/calendarserver/changeset/7802
Author:   cdaboo at apple.com
Date:     2011-07-15 08:56:51 -0700 (Fri, 15 Jul 2011)
Log Message:
-----------
Batch multiget requests like clients do.

Modified Paths:
--------------
    CalendarServer/trunk/contrib/performance/loadtest/ical.py
    CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report.request
    CalendarServer/trunk/contrib/performance/loadtest/test_ical.py

Added Paths:
-----------
    CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report_href.request

Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/ical.py	2011-07-15 14:47:18 UTC (rev 7801)
+++ CalendarServer/trunk/contrib/performance/loadtest/ical.py	2011-07-15 15:56:51 UTC (rev 7802)
@@ -181,6 +181,9 @@
     # configuration.  This is also the actual value used by Snow
     # Leopard iCal.
     CALENDAR_HOME_POLL_INTERVAL = 15 * 60
+    
+    # The maximum number of resources to retrieve in a single multiget
+    MULTIGET_BATCH_SIZE = 200
 
     _STARTUP_PRINCIPAL_PROPFIND = loadRequestBody('sl_startup_principal_propfind')
     _STARTUP_PRINCIPALS_REPORT = loadRequestBody('sl_startup_principals_report')
@@ -190,6 +193,7 @@
 
     _CALENDAR_PROPFIND = loadRequestBody('sl_calendar_propfind')
     _CALENDAR_REPORT = loadRequestBody('sl_calendar_report')
+    _CALENDAR_REPORT_HREF = loadRequestBody('sl_calendar_report_href')
 
     _USER_LIST_PRINCIPAL_PROPERTY_SEARCH = loadRequestBody('sl_user_list_principal_property_search')
     _POST_AVAILABILITY = loadRequestBody('sl_post_availability')
@@ -396,6 +400,7 @@
         body = yield readBody(response)
 
         result = self._parseMultiStatus(body)
+        changed = []
         for responseHref in result:
             if responseHref == calendar.url:
                 continue
@@ -411,9 +416,17 @@
                 
             event = self._events[responseHref]
             if event.etag != etag:
-                response = yield self._eventReport(url, responseHref)
-                body = yield readBody(response)
-                res = self._parseMultiStatus(body)[responseHref]
+                changed.append(responseHref)
+            
+        while changed:
+            batchedHrefs = changed[:self.MULTIGET_BATCH_SIZE]
+            changed = changed[self.MULTIGET_BATCH_SIZE:]
+    
+            response = yield self._eventReport(url, batchedHrefs)
+            body = yield readBody(response)
+            multistatus = self._parseMultiStatus(body)
+            for responseHref in batchedHrefs:
+                res = multistatus[responseHref]
                 if res.getStatus() is None or " 404 " not in res.getStatus():
                     text = res.getTextProperties()
                     etag = text[davxml.getetag]
@@ -434,15 +447,16 @@
         self.catalog["eventChanged"].issue(href)
 
                 
-    def _eventReport(self, calendar, event):
-        # Next do a REPORT on each event that might have information
+    def _eventReport(self, calendar, events):
+        # Next do a REPORT on events that might have information
         # we don't know about.
+        hrefs = "".join([self._CALENDAR_REPORT_HREF % {'href': event} for event in events])
         return self._request(
             MULTI_STATUS,
             'REPORT',
             self.root + calendar,
             Headers({'content-type': ['text/xml']}),
-            StringProducer(self._CALENDAR_REPORT % {'href': event}))
+            StringProducer(self._CALENDAR_REPORT % {'hrefs': hrefs}))
 
 
     def _checkCalendarsForEvents(self, calendarHomeSet):

Modified: CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report.request	2011-07-15 14:47:18 UTC (rev 7801)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report.request	2011-07-15 15:56:51 UTC (rev 7802)
@@ -1,2 +1,9 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<x0:calendar-multiget xmlns:x0="urn:ietf:params:xml:ns:caldav" xmlns:x1="DAV:"><x1:prop><x1:getetag/><x0:calendar-data/><x0:schedule-tag/></x1:prop><x1:href>%(href)s</x1:href></x0:calendar-multiget>
+<x0:calendar-multiget xmlns:x0="urn:ietf:params:xml:ns:caldav" xmlns:x1="DAV:">
+  <x1:prop>
+    <x1:getetag/>
+    <x0:calendar-data/>
+    <x0:schedule-tag/>
+  </x1:prop>
+%(hrefs)s
+</x0:calendar-multiget>

Added: CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report_href.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report_href.request	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/sl_calendar_report_href.request	2011-07-15 15:56:51 UTC (rev 7802)
@@ -0,0 +1 @@
+  <x1:href>%(href)s</x1:href>

Modified: CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_ical.py	2011-07-15 14:47:18 UTC (rev 7801)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_ical.py	2011-07-15 15:56:51 UTC (rev 7802)
@@ -971,10 +971,10 @@
         self.client._events[event.url] = event
         self.client.changeEventAttendee(event.url, old, new)
 
-        result, req = requests.pop(0)
+        _ignore_result, req = requests.pop(0)
 
         # iCal PUTs the new VCALENDAR object.
-        expectedResponseCode, method, url, headers, body = req
+        _ignore_expectedResponseCode, method, url, headers, body = req
         self.assertEquals(method, 'PUT')
         self.assertEquals(url, 'http://127.0.0.1' + event.url)
         self.assertIsInstance(url, str)
@@ -1146,8 +1146,87 @@
     <href>/something/anotherthing.ics</href>
     <status>HTTP/1.1 404 Not Found</status>
   </response>
+  <response>
+    <href>/something/else.ics</href>
+    <propstat>
+      <prop>
+        <getetag>"ef70beb4cb7da4b2e2950350b09e9a01"</getetag>
+        <calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+BEGIN:VEVENT
+UID:CD54161A13AA8A4649D3781E at caldav.corp.apple.com
+DTSTART:20110715T140000Z
+DURATION:PT1H
+DTSTAMP:20110715T144217Z
+SUMMARY:Test2
+END:VEVENT
+END:VCALENDAR
+]]></calendar-data>
+      </prop>
+      <status>HTTP/1.1 200 OK</status>
+    </propstat>
+  </response>
 </multistatus>
 """
+
+    _CALENDAR_REPORT_RESPONSE_BODY_1 = """\
+<?xml version='1.0' encoding='UTF-8'?>
+<multistatus xmlns='DAV:'>
+  <response>
+    <href>/something/anotherthing.ics</href>
+    <propstat>
+      <prop>
+        <getetag>"ef70beb4cb7da4b2e2950350b09e9a01"</getetag>
+        <calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+BEGIN:VEVENT
+UID:anotherthing at caldav.corp.apple.com
+DTSTART:20110715T140000Z
+DURATION:PT1H
+DTSTAMP:20110715T144217Z
+SUMMARY:Test1
+END:VEVENT
+END:VCALENDAR
+]]></calendar-data>
+      </prop>
+      <status>HTTP/1.1 200 OK</status>
+    </propstat>
+  </response>
+</multistatus>
+"""
+
+    _CALENDAR_REPORT_RESPONSE_BODY_2 = """\
+<?xml version='1.0' encoding='UTF-8'?>
+<multistatus xmlns='DAV:'>
+  <response>
+    <href>/something/else.ics</href>
+    <propstat>
+      <prop>
+        <getetag>"ef70beb4cb7da4b2e2950350b09e9a01"</getetag>
+        <calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+BEGIN:VEVENT
+UID:else at caldav.corp.apple.com
+DTSTART:20110715T140000Z
+DURATION:PT1H
+DTSTAMP:20110715T144217Z
+SUMMARY:Test2
+END:VEVENT
+END:VCALENDAR
+]]></calendar-data>
+      </prop>
+      <status>HTTP/1.1 200 OK</status>
+    </propstat>
+  </response>
+</multistatus>
+"""
+
     def test_eventMissing(self):
         """
         If an event included in the calendar PROPFIND response no longer exists
@@ -1160,7 +1239,7 @@
         self.client._calendars[calendar.url] = calendar
         self.client._updateCalendar(calendar)
         result, req = requests.pop(0)
-        expectedResponseCode, method, url, headers, body = req
+        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
         self.assertEqual('PROPFIND', method)
         self.assertEqual('http://127.0.0.1/something/', url)
         self.assertEqual(MULTI_STATUS, expectedResponseCode)
@@ -1171,7 +1250,7 @@
                 StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
         
         result, req = requests.pop(0)
-        expectedResponseCode, method, url, headers, body = req
+        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
         self.assertEqual('REPORT', method)
         self.assertEqual('http://127.0.0.1/something/', url)
         self.assertEqual(MULTI_STATUS, expectedResponseCode)
@@ -1189,7 +1268,59 @@
         self.assertIn('/something/else.ics', self.client._events)
 
 
+    def test_multigetBatch(self):
+        """
+        If an event included in the calendar PROPFIND response no longer exists
+        by the time a REPORT is issued for that event, the 404 is handled and
+        the rest of the normal update logic for that event is skipped.
+        """
+        requests = self.interceptRequests()
 
+        self.patch(self.client, "MULTIGET_BATCH_SIZE", 1)
+
+        calendar = Calendar(None, 'calendar', '/something/', None)
+        self.client._calendars[calendar.url] = calendar
+        self.client._updateCalendar(calendar)
+        result, req = requests.pop(0)
+        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
+        self.assertEqual('PROPFIND', method)
+        self.assertEqual('http://127.0.0.1/something/', url)
+        self.assertEqual(MULTI_STATUS, expectedResponseCode)
+
+        result.callback(
+            MemoryResponse(
+                ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+                StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
+        
+        result, req = requests.pop(0)
+        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
+        self.assertEqual('REPORT', method)
+        self.assertEqual('http://127.0.0.1/something/', url)
+        self.assertEqual(MULTI_STATUS, expectedResponseCode)
+
+        result.callback(
+            MemoryResponse(
+                ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+                StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_1)))
+
+        self.assertTrue(self.client._events['/something/anotherthing.ics'].etag is not None)
+        self.assertTrue(self.client._events['/something/else.ics'].etag is None)
+        
+        result, req = requests.pop(0)
+        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
+        self.assertEqual('REPORT', method)
+        self.assertEqual('http://127.0.0.1/something/', url)
+        self.assertEqual(MULTI_STATUS, expectedResponseCode)
+
+        result.callback(
+            MemoryResponse(
+                ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+                StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_2)))
+
+        self.assertTrue(self.client._events['/something/anotherthing.ics'].etag is not None)
+        self.assertTrue(self.client._events['/something/else.ics'].etag is not None)
+
+
 class VFreeBusyTests(SnowLeopardMixin, TestCase):
     """
     Tests for L{SnowLeopard.requestAvailability}.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110715/acd89ece/attachment-0001.html>


More information about the calendarserver-changes mailing list