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

source_changes at macosforge.org source_changes at macosforge.org
Wed Feb 23 10:11:25 PST 2011


Revision: 7070
          http://trac.macosforge.org/projects/calendarserver/changeset/7070
Author:   exarkun at twistedmatrix.com
Date:     2011-02-23 10:11:24 -0800 (Wed, 23 Feb 2011)
Log Message:
-----------
Remove some bogus error handling from ical.SnowLeopard; start adding configurable observer stuff.

Modified Paths:
--------------
    CalendarServer/trunk/contrib/performance/loadtest/config.plist
    CalendarServer/trunk/contrib/performance/loadtest/ical.py
    CalendarServer/trunk/contrib/performance/loadtest/population.py
    CalendarServer/trunk/contrib/performance/loadtest/sim.py
    CalendarServer/trunk/contrib/performance/loadtest/test_sim.py

Modified: CalendarServer/trunk/contrib/performance/loadtest/config.plist
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/config.plist	2011-02-22 20:46:57 UTC (rev 7069)
+++ CalendarServer/trunk/contrib/performance/loadtest/config.plist	2011-02-23 18:11:24 UTC (rev 7070)
@@ -63,5 +63,10 @@
       </dict>
     </array>
 
+    <key>observers</key>
+    <array>
+      <string>loadtest.population.ReportStatistics</string>
+    </array>
+
   </dict>
 </plist>

Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/ical.py	2011-02-22 20:46:57 UTC (rev 7069)
+++ CalendarServer/trunk/contrib/performance/loadtest/ical.py	2011-02-23 18:11:24 UTC (rev 7070)
@@ -51,7 +51,22 @@
 
 SUPPORTED_REPORT_SET = '{DAV:}supported-report-set'
 
+class IncorrectResponseCode(Exception):
+    """
+    Raised when a response has a code other than the one expected.
 
+    @ivar expected: The response code which was expected.
+    @type expected: C{int}
+
+    @ivar response: The response which was received
+    @type response: L{twisted.web.client.Response}
+    """
+    def __init__(self, expected, response):
+        self.expected = expected
+        self.response = response
+
+
+
 class Event(object):
     def __init__(self, url, etag, vevent=None):
         self.url = url
@@ -123,7 +138,10 @@
 
     USER_AGENT = "DAVKit/4.0.3 (732); CalendarStore/4.0.3 (991); iCal/4.0.3 (1388); Mac OS X/10.6.4 (10F569)"
 
-    CALENDAR_HOME_POLL_INTERVAL = 60 * 3
+    # The default interval, used if none is specified in external
+    # configuration.  This is also the actual value used by Snow
+    # Leopard iCal.
+    CALENDAR_HOME_POLL_INTERVAL = 60 * 15
 
     _STARTUP_PRINCIPAL_PROPFIND = loadRequestBody('sl_startup_principal_propfind')
     _STARTUP_PRINCIPALS_REPORT = loadRequestBody('sl_startup_principals_report')
@@ -139,12 +157,16 @@
 
     email = None
 
-    def __init__(self, reactor, host, port, user, auth):
+    def __init__(self, reactor, host, port, user, auth, calendarHomePollInterval=None):
         self.reactor = reactor
         self.agent = AuthHandlerAgent(Agent(self.reactor), auth)
         self.root = 'http://%s:%d/' % (host, port)
         self.user = user
 
+        if calendarHomePollInterval is None:
+            calendarHomePollInterval = self.CALENDAR_HOME_POLL_INTERVAL
+        self.calendarHomePollInterval = calendarHomePollInterval
+
         # Keep track of the calendars on this account, keys are
         # Calendar URIs, values are Calendar instances.
         self._calendars = {}
@@ -169,18 +191,26 @@
         d = self.agent.request(method, url, headers, body)
         before = self.reactor.seconds()
         def report(response):
+            # XXX This is time to receive response headers, not time
+            # to receive full response.  Should measure the latter, if
+            # not both.
+            after = self.reactor.seconds()
+
+            # XXX If the response code is wrong, there's probably not
+            # point passing the response down the callback chain.
+            # errback?
             success = response.code == expectedResponseCode
+
             # if not success:
             #     import pdb; pdb.set_trace()
-            after = self.reactor.seconds()
-            # XXX This is time to receive response headers, not time
-            # to receive full response.  Should measure the latter, if
-            # not both.
             msg(
                 type="response", success=success, method=method,
                 headers=headers, body=body,
                 duration=(after - before), url=url)
-            return response
+
+            if success:
+                return response
+            raise IncorrectResponseCode(expectedResponseCode, response)
         d.addCallback(report)
         return d
 
@@ -357,7 +387,11 @@
             for cal in calendars:
                 if self._calendars.setdefault(cal.url, cal).ctag != cal.ctag or True:
                     self._updateCalendar(cal)
-        d.addCallback(cbCalendars)
+        def ebCalendars(reason):
+            reason.trap(IncorrectResponseCode)
+            msg(type="aggregate", operation="poll", success=False)
+        d.addCallbacks(cbCalendars, ebCalendars)
+        d.addErrback(err, "Unexpected failure during calendar home poll")
         return d
 
 
@@ -395,11 +429,7 @@
     @inlineCallbacks
     def startup(self):
         # Orient ourselves, or something
-        try:
-            principal = yield self._principalPropfind(self.user)
-        except:
-            err(None, "Startup principal PROPFIND failed")
-            return
+        principal = yield self._principalPropfind(self.user)
 
         hrefs = principal.getHrefProperties()
 
@@ -431,12 +461,11 @@
 
     def _calendarCheckLoop(self, calendarHome):
         """
-        Poll Calendar Home (and notifications?) every 15 (or whatever)
-        minutes
+        Periodically check the calendar home for changes to calendars.
         """
         pollCalendarHome = LoopingCall(
             self._checkCalendarsForEvents, calendarHome)
-        pollCalendarHome.start(self.CALENDAR_HOME_POLL_INTERVAL)
+        pollCalendarHome.start(self.calendarHomePollInterval)
 
 
     @inlineCallbacks
@@ -446,9 +475,9 @@
         """
         principal = yield self.startup()
         hrefs = principal.getHrefProperties()
-
         self._calendarCheckLoop(hrefs[caldavxml.calendar_home_set].toString())
 
+        # XXX Oops, should probably stop sometime.
         yield Deferred()
 
 

Modified: CalendarServer/trunk/contrib/performance/loadtest/population.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/population.py	2011-02-22 20:46:57 UTC (rev 7069)
+++ CalendarServer/trunk/contrib/performance/loadtest/population.py	2011-02-23 18:11:24 UTC (rev 7070)
@@ -23,7 +23,7 @@
 from itertools import izip
 
 from twisted.python.util import FancyEqMixin
-from twisted.python.log import msg
+from twisted.python.log import msg, err
 
 from stats import mean, median, stddev, mad
 from loadtest.ical import SnowLeopard, RequestLogger
@@ -147,14 +147,23 @@
             clientType = self._pop.next()
             client = clientType.clientType(
                 self.reactor, self.host, self.port, user, auth)
-            client.run()
+            d = client.run()
+            d.addCallbacks(self._clientSuccess, self._clientFailure)
 
             for profileType in clientType.profileTypes:
                 profileType(self.reactor, client, number).run()
         msg(type="status", clientCount=self._user - 1)
 
 
+    def _clientSuccess(self, result):
+        pass
 
+
+    def _clientFailure(self, reason):
+        err(reason, "Client stopped with error")
+
+
+
 class SmoothRampUp(object):
     def __init__(self, reactor, groups, groupSize, interval):
         self.reactor = reactor

Modified: CalendarServer/trunk/contrib/performance/loadtest/sim.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/sim.py	2011-02-22 20:46:57 UTC (rev 7069)
+++ CalendarServer/trunk/contrib/performance/loadtest/sim.py	2011-02-23 18:11:24 UTC (rev 7070)
@@ -21,6 +21,7 @@
 from collections import namedtuple
 
 from twisted.python.filepath import FilePath
+from twisted.python.log import addObserver
 from twisted.python.usage import UsageError, Options
 from twisted.python.reflect import namedAny
 
@@ -95,12 +96,13 @@
     @type arrival: L{Arrival}
     @type parameters: L{PopulationParameters}
     """
-    def __init__(self, server, arrival, parameters, reactor=None):
+    def __init__(self, server, arrival, parameters, observers=None, reactor=None):
         if reactor is None:
             from twisted.internet import reactor
         self.server = server
         self.arrival = arrival
         self.parameters = parameters
+        self.observers = observers
         self.reactor = reactor
 
 
@@ -130,7 +132,6 @@
             arrival = Arrival(
                 SmoothRampUp, dict(groups=10, groupSize=1, interval=3))
 
-
         parameters = PopulationParameters()
         if 'clients' in options.config:
             for clientConfig in options.config['clients']:
@@ -144,9 +145,14 @@
             parameters.addClient(
                 1, ClientType(SnowLeopard, [Eventer, Inviter, Accepter]))
 
-        return cls(server, arrival, parameters)
+        observers = []
+        if 'observers' in options.config:
+            for observerName in options.config['observers']:
+                observers.append(namedAny(observerName)())
 
+        return cls(server, arrival, parameters, observers)
 
+
     @classmethod
     def main(cls, args=None):
         simulator = cls.fromCommandLine(args)
@@ -170,6 +176,7 @@
         arrivalPolicy = self.createArrivalPolicy()
         arrivalPolicy.run(sim)
         self.reactor.run()
+        for obs in self.observers:
+            obs.report()
 
-
 main = LoadSimulator.main

Modified: CalendarServer/trunk/contrib/performance/loadtest/test_sim.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_sim.py	2011-02-22 20:46:57 UTC (rev 7069)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_sim.py	2011-02-23 18:11:24 UTC (rev 7070)
@@ -15,8 +15,10 @@
 #
 ##
 
+from operator import setitem
 from plistlib import writePlistToString
 
+from twisted.python.log import LogPublisher, theLogPublisher
 from twisted.python.usage import UsageError
 from twisted.python.filepath import FilePath
 from twisted.trial.unittest import TestCase
@@ -26,7 +28,8 @@
 from loadtest.ical import SnowLeopard
 from loadtest.profiles import Eventer, Inviter, Accepter
 from loadtest.population import (
-    SmoothRampUp, ClientType, PopulationParameters, CalendarClientSimulator)
+    SmoothRampUp, ClientType, PopulationParameters, CalendarClientSimulator,
+    SimpleStatistics)
 from loadtest.sim import Server, Arrival, SimOptions, LoadSimulator, main
 
 VALID_CONFIG = {
@@ -90,6 +93,32 @@
 
 
 
+class Reactor(object):
+    def run(self):
+        pass
+
+
+class Observer(object):
+    def __init__(self):
+        self.reported = False
+        self.events = []
+
+
+    def observe(self, event):
+        self.events.append(event)
+
+
+    def report(self):
+        self.reported = True
+
+
+
+class NullArrival(object):
+    def run(self, sim):
+        pass
+
+
+
 class StubSimulator(LoadSimulator):
     def run(self):
         return 3
@@ -107,7 +136,8 @@
 
         exc = self.assertRaises(
             SystemExit, StubSimulator.main, ['--config', config.path])
-        self.assertEquals(exc.args, (StubSimulator(None, None, None).run(),))
+        self.assertEquals(
+            exc.args, (StubSimulator(None, None, None).run(),))
 
 
     def test_createSimulator(self):
@@ -119,7 +149,7 @@
         host = '127.0.0.7'
         port = 1243
         reactor = object()
-        sim = LoadSimulator(Server(host, port), None, None, reactor)
+        sim = LoadSimulator(Server(host, port), None, None, reactor=reactor)
         calsim = sim.createSimulator()
         self.assertIsInstance(calsim, CalendarClientSimulator)
         self.assertIdentical(calsim.reactor, reactor)
@@ -176,7 +206,7 @@
 
         reactor = object()
         sim = LoadSimulator(
-            None, Arrival(FakeArrival, {'x': 3, 'y': 2}), None, reactor)
+            None, Arrival(FakeArrival, {'x': 3, 'y': 2}), None, reactor=reactor)
         arrival = sim.createArrivalPolicy()
         self.assertIsInstance(arrival, FakeArrival)
         self.assertIdentical(arrival.reactor, reactor)
@@ -215,3 +245,38 @@
         expectedParameters.addClient(
             1, ClientType(SnowLeopard, [Eventer, Inviter, Accepter]))
         self.assertEquals(sim.parameters, expectedParameters)
+
+
+    def test_loadLogObservers(self):
+        """
+        Log observers specified in the [observers] section of the
+        configuration file are added to the logging system.
+        """
+        config = FilePath(self.mktemp())
+        config.setContent(writePlistToString({
+                    "observers": ["loadtest.population.SimpleStatistics"]}))
+        sim = LoadSimulator.fromCommandLine(['--config', config.path])
+        self.assertEquals(len(sim.observers), 1)
+        self.assertIsInstance(sim.observers[0], SimpleStatistics)
+
+    def test_observeBeforeRun(self):
+        """
+        Each log observer is added to the log publisher before the
+        simulation run is started.
+        """
+        self.fail("implement me")
+
+
+    def test_reportAfterRun(self):
+        """
+        Each log observer also has its C{report} method called after
+        the simulation run completes.
+        """
+        observers = [Observer()]
+        sim = LoadSimulator(
+            Server('example.com', 123), 
+            Arrival(lambda reactor: NullArrival(), {}),
+            None, observers, Reactor())
+        sim.run()
+        self.assertTrue(observers[0].reported)
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110223/c80679aa/attachment-0001.html>


More information about the calendarserver-changes mailing list