[CalendarServer-changes] [7806] CalendarServer/trunk/contrib/performance/loadtest
source_changes at macosforge.org
source_changes at macosforge.org
Tue Jul 19 20:12:26 PDT 2011
Revision: 7806
http://trac.macosforge.org/projects/calendarserver/changeset/7806
Author: cdaboo at apple.com
Date: 2011-07-19 20:12:26 -0700 (Tue, 19 Jul 2011)
Log Message:
-----------
Allow profiles to be disabled via config parameter. Clean/format some XML.
Modified Paths:
--------------
CalendarServer/trunk/contrib/performance/loadtest/config.plist
CalendarServer/trunk/contrib/performance/loadtest/population.py
CalendarServer/trunk/contrib/performance/loadtest/profiles.py
CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
Modified: CalendarServer/trunk/contrib/performance/loadtest/config.plist
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/config.plist 2011-07-19 21:38:22 UTC (rev 7805)
+++ CalendarServer/trunk/contrib/performance/loadtest/config.plist 2011-07-20 03:12:26 UTC (rev 7806)
@@ -18,268 +18,261 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
- <dict>
- <!-- Identify the server to be load tested. -->
- <key>server</key>
- <string>https://127.0.0.1:8443/</string>
+ <dict>
+ <!-- Identify the server to be load tested. -->
+ <key>server</key>
+ <string>https://127.0.0.1:8443/</string>
- <!-- Define the credentials of the clients which will be used to load test
- the server. These credentials must already be valid on the
- server. -->
- <key>accounts</key>
- <dict>
- <!-- The loader is the fully-qualified Python name of a callable which
- returns a list of directory service records defining all of the
- client accounts to use.
+ <!-- Define the credentials of the clients which will be used to load test
+ the server. These credentials must already be valid on the server. -->
+ <key>accounts</key>
+ <dict>
+ <!-- The loader is the fully-qualified Python name of a callable which
+ returns a list of directory service records defining all of the client accounts
+ to use. contrib.performance.loadtest.sim.recordsFromCSVFile reads username,
+ password, mailto triples from a CSV file and returns them as a list of faked
+ directory service records. -->
+ <key>loader</key>
+ <string>contrib.performance.loadtest.sim.recordsFromCSVFile</string>
- contrib.performance.loadtest.sim.recordsFromCSVFile reads username, password, mailto
- triples from a CSV file and returns them as a list of faked
- directory service records. -->
- <key>loader</key>
- <string>contrib.performance.loadtest.sim.recordsFromCSVFile</string>
+ <!-- Keyword arguments may be passed to the loader. -->
+ <key>params</key>
+ <dict>
+ <!-- recordsFromCSVFile interprets the path relative to the config.plist,
+ to make it independent of the script's working directory while still allowing
+ a relative path. This isn't a great solution. -->
+ <key>path</key>
+ <string>contrib/performance/loadtest/accounts.csv</string>
+ </dict>
+ </dict>
- <!-- Keyword arguments may be passed to the loader. -->
- <key>params</key>
- <dict>
- <!-- recordsFromCSVFile interprets the path relative to the
- config.plist, to make it independent of the script's working
- directory while still allowing a relative path. This isn't
- a great solution. -->
- <key>path</key>
- <string>contrib/performance/loadtest/accounts.csv</string>
- </dict>
- </dict>
+ <!-- Define how many clients will participate in the load test and how
+ they will show up. -->
+ <key>arrival</key>
+ <dict>
- <!-- Define how many clients will participate in the load test and how they
- will show up. -->
- <key>arrival</key>
- <dict>
+ <!-- Specify a class which creates new clients and introduces them into
+ the test. contrib.performance.loadtest.population.SmoothRampUp introduces
+ groups of new clients at fixed intervals up to a maximum. The size of the
+ group, interval, and maximum are configured by the parameters below. The
+ total number of clients is groups * groupSize, which needs to be no larger
+ than the number of credentials created in the accounts section. -->
+ <key>factory</key>
+ <string>contrib.performance.loadtest.population.SmoothRampUp</string>
- <!-- Specify a class which creates new clients and introduces them into the test.
+ <key>params</key>
+ <dict>
+ <!-- groups gives the total number of groups of clients to introduce. -->
+ <key>groups</key>
+ <integer>99</integer>
- contrib.performance.loadtest.population.SmoothRampUp introduces groups of new clients at
- fixed intervals up to a maximum. The size of the group, interval,
- and maximum are configured by the parameters below. The total
- number of clients is groups * groupSize, which needs to be no larger
- than the number of credentials created in the accounts section. -->
- <key>factory</key>
- <string>contrib.performance.loadtest.population.SmoothRampUp</string>
+ <!-- groupSize is the number of clients in each group of clients. It's
+ really only a "smooth" ramp up if this is pretty small. -->
+ <key>groupSize</key>
+ <integer>1</integer>
- <key>params</key>
- <dict>
- <!-- groups gives the total number of groups of clients to introduce.-->
- <key>groups</key>
- <integer>99</integer>
+ <!-- Number of seconds between the introduction of each group. -->
+ <key>interval</key>
+ <integer>3</integer>
+ </dict>
- <!-- groupSize is the number of clients in each group of clients. It's
- really only a "smooth" ramp up if this is pretty small. -->
- <key>groupSize</key>
- <integer>1</integer>
+ </dict>
- <!-- Number of seconds between the introduction of each group. -->
- <key>interval</key>
- <integer>3</integer>
- </dict>
+ <!-- Define the kinds of software and user behavior the load simulation
+ will simulate. -->
+ <key>clients</key>
- </dict>
+ <!-- Have as many different kinds of software and user behavior configurations
+ as you want. Each is a dict -->
+ <array>
- <!-- Define the kinds of software and user behavior the load simulation
- will simulate. -->
- <key>clients</key>
+ <dict>
- <!-- Have as many different kinds of software and user behavior
- configurations as you want. Each is a dict -->
- <array>
+ <!-- Here is a Snow Leopard iCal simulator. -->
+ <key>software</key>
+ <string>contrib.performance.loadtest.ical.SnowLeopard</string>
- <dict>
+ <!-- Arguments to use to initialize the SnowLeopard instance. -->
+ <key>params</key>
+ <dict>
+ <!-- SnowLeopard can poll the calendar home at some interval. This is
+ in seconds. -->
+ <key>calendarHomePollInterval</key>
+ <integer>30</integer>
- <!-- Here is a Snow Leopard iCal simulator. -->
- <key>software</key>
- <string>contrib.performance.loadtest.ical.SnowLeopard</string>
+ <!-- If the server advertises xmpp push, SnowLeopard can wait for notifications
+ about calendar home changes instead of polling for them periodically. If
+ this option is true, then look for the server advertisement for xmpp push
+ and use it if possible. Still fall back to polling if there is no xmpp push
+ advertised. -->
+ <key>supportPush</key>
+ <false />
+ </dict>
- <!-- Arguments to use to initialize the SnowLeopard instance. -->
- <key>params</key>
- <dict>
- <!-- SnowLeopard can poll the calendar home at some interval. This
- is in seconds. -->
- <key>calendarHomePollInterval</key>
- <integer>30</integer>
+ <!-- The profiles define certain types of user behavior on top of the
+ client software being simulated. -->
+ <key>profiles</key>
+ <array>
- <!-- If the server advertises xmpp push, SnowLeopard can wait for
- notifications about calendar home changes instead of polling for
- them periodically. If this option is true, then look for the
- server advertisement for xmpp push and use it if possible.
- Still fall back to polling if there is no xmpp push
- advertised. -->
- <key>supportPush</key>
- <false/>
- </dict>
+ <!-- First an event-creating profile, which will periodically create
+ new events at a random time on a random calendar. -->
+ <dict>
+ <key>class</key>
+ <string>contrib.performance.loadtest.profiles.Eventer</string>
- <!-- The profiles define certain types of user behavior on top of the
- client software being simulated. -->
- <key>profiles</key>
- <array>
+ <key>params</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
- <!-- First an event-creating profile, which will periodically create
- new events at a random time on a random calendar. -->
- <dict>
- <key>class</key>
- <string>contrib.performance.loadtest.profiles.Eventer</string>
+ <!-- Define the interval (in seconds) at which this profile will use
+ its client to create a new event. -->
+ <key>interval</key>
+ <integer>60</integer>
- <key>params</key>
- <dict>
- <!-- Define the interval (in seconds) at which this profile will
- use its client to create a new event. -->
- <key>interval</key>
- <integer>60</integer>
+ <!-- Define how start times (DTSTART) for the randomly generated events
+ will be selected. This is an example of a "Distribution" parameter. The value
+ for most "Distribution" parameters are interchangeable and extensible. -->
+ <key>eventStartDistribution</key>
+ <dict>
- <!-- Define how start times (DTSTART) for the randomly generated
- events will be selected. This is an example of a
- "Distribution" parameter. The value for most "Distribution"
- parameters are interchangeable and extensible. -->
- <key>eventStartDistribution</key>
- <dict>
+ <!-- This distribution is pretty specialized. It produces timestamps
+ in the near future, limited to certain days of the week and certain hours
+ of the day. -->
+ <key>type</key>
+ <string>contrib.performance.stats.WorkDistribution</string>
- <!-- This distribution is pretty specialized. It produces
- timestamps in the near future, limited to certain days of
- the week and certain hours of the day. -->
- <key>type</key>
- <string>contrib.performance.stats.WorkDistribution</string>
+ <key>params</key>
+ <dict>
+ <!-- These are the days of the week the distribution will use. -->
+ <key>daysOfWeek</key>
+ <array>
+ <string>mon</string>
+ <string>tue</string>
+ <string>wed</string>
+ <string>thu</string>
+ <string>fri</string>
+ </array>
- <key>params</key>
- <dict>
- <!-- These are the days of the week the distribution will
- use. -->
- <key>daysOfWeek</key>
- <array>
- <string>mon</string>
- <string>tue</string>
- <string>wed</string>
- <string>thu</string>
- <string>fri</string>
- </array>
+ <!-- The earliest hour of a day at which an event might be scheduled. -->
+ <key>beginHour</key>
+ <integer>8</integer>
- <!-- The earliest hour of a day at which an event might be
- scheduled. -->
- <key>beginHour</key>
- <integer>8</integer>
+ <!-- And the latest hour of a day (at which an event will be scheduled
+ to begin!). -->
+ <key>endHour</key>
+ <integer>16</integer>
- <!-- And the latest hour of a day (at which an event will be
- scheduled to begin!). -->
- <key>endHour</key>
- <integer>16</integer>
+ <!-- The timezone in which the event is scheduled. (XXX Does this
+ really work right?) -->
+ <key>tzname</key>
+ <string>America/Los_Angeles</string>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
- <!-- The timezone in which the event is scheduled.
- (XXX Does this really work right?) -->
- <key>tzname</key>
- <string>America/Los_Angeles</string>
- </dict>
- </dict>
- </dict>
- </dict>
+ <!-- This profile invites new attendees to existing events. -->
+ <dict>
+ <key>class</key>
+ <string>contrib.performance.loadtest.profiles.Inviter</string>
- <!-- This profile invites new attendees to existing events. -->
- <dict>
- <key>class</key>
- <string>contrib.performance.loadtest.profiles.Inviter</string>
-
- <key>params</key>
- <dict>
- <!-- Define the frequency at which new invitations will be sent
- out. -->
- <key>sendInvitationDistribution</key>
- <dict>
- <key>type</key>
- <string>contrib.performance.stats.NormalDistribution</string>
- <key>params</key>
- <dict>
- <!-- mu gives the mean of the normal distribution (in
- seconds). -->
- <key>mu</key>
- <integer>60</integer>
+ <key>params</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
- <!-- and sigma gives its standard deviation. -->
- <key>sigma</key>
- <integer>5</integer>
- </dict>
- </dict>
+ <!-- Define the frequency at which new invitations will be sent out. -->
+ <key>sendInvitationDistribution</key>
+ <dict>
+ <key>type</key>
+ <string>contrib.performance.stats.NormalDistribution</string>
+ <key>params</key>
+ <dict>
+ <!-- mu gives the mean of the normal distribution (in seconds). -->
+ <key>mu</key>
+ <integer>60</integer>
- <!-- Define the distribution of who will be invited to an event.
- Each set of credentials loaded by the load tester has an
- index; samples from this distribution will be added to that
- index to arrive at the index of some other credentials,
- which will be the target of the invitation. -->
- <key>inviteeDistanceDistribution</key>
- <dict>
- <key>type</key>
- <string>contrib.performance.stats.UniformIntegerDistribution</string>
- <key>params</key>
- <dict>
- <!-- The minimum value (inclusive) of the uniform
- distribution. -->
- <key>min</key>
- <integer>-100</integer>
- <!-- The maximum value (exclusive) of the uniform
- distribution. -->
- <key>max</key>
- <integer>101</integer>
- </dict>
- </dict>
- </dict>
+ <!-- and sigma gives its standard deviation. -->
+ <key>sigma</key>
+ <integer>5</integer>
+ </dict>
+ </dict>
- </dict>
+ <!-- Define the distribution of who will be invited to an event. Each
+ set of credentials loaded by the load tester has an index; samples from this
+ distribution will be added to that index to arrive at the index of some other
+ credentials, which will be the target of the invitation. -->
+ <key>inviteeDistanceDistribution</key>
+ <dict>
+ <key>type</key>
+ <string>contrib.performance.stats.UniformIntegerDistribution</string>
+ <key>params</key>
+ <dict>
+ <!-- The minimum value (inclusive) of the uniform distribution. -->
+ <key>min</key>
+ <integer>-100</integer>
+ <!-- The maximum value (exclusive) of the uniform distribution. -->
+ <key>max</key>
+ <integer>101</integer>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
- <!-- This profile accepts invitations to events. -->
- <dict>
- <key>class</key>
- <string>contrib.performance.loadtest.profiles.Accepter</string>
+ <!-- This profile accepts invitations to events, handles cancels, and
+ handles replies received. -->
+ <dict>
+ <key>class</key>
+ <string>contrib.performance.loadtest.profiles.Accepter</string>
- <key>params</key>
- <dict>
- <!-- Define how long to wait after seeing a new invitation before
- accepting it. -->
- <key>acceptDelayDistribution</key>
- <dict>
- <key>type</key>
- <string>contrib.performance.stats.NormalDistribution</string>
- <key>params</key>
- <dict>
- <!-- mean -->
- <key>mu</key>
- <integer>360</integer>
- <!-- standard deviation -->
- <key>sigma</key>
- <integer>60</integer>
- </dict>
- </dict>
- </dict>
-
- </dict>
- </array>
+ <key>params</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
- <!-- Determine the frequency at which this client configuration will
- appear in the clients which are created by the load tester. -->
- <key>weight</key>
- <integer>1</integer>
- </dict>
- </array>
+ <!-- Define how long to wait after seeing a new invitation before
+ accepting it. -->
+ <key>acceptDelayDistribution</key>
+ <dict>
+ <key>type</key>
+ <string>contrib.performance.stats.NormalDistribution</string>
+ <key>params</key>
+ <dict>
+ <!-- mean -->
+ <key>mu</key>
+ <integer>360</integer>
+ <!-- standard deviation -->
+ <key>sigma</key>
+ <integer>60</integer>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+ </array>
- <!-- Define some log observers to report on the load test. -->
- <key>observers</key>
- <array>
- <!-- ReportStatistics generates an end-of-run summary of the HTTP
- requests made, their timings, and their results. -->
- <string>contrib.performance.loadtest.population.ReportStatistics</string>
+ <!-- Determine the frequency at which this client configuration will
+ appear in the clients which are created by the load tester. -->
+ <key>weight</key>
+ <integer>1</integer>
+ </dict>
+ </array>
- <!-- RequestLogger generates a realtime log of all HTTP requests made
- during the load test. -->
- <string>contrib.performance.loadtest.ical.RequestLogger</string>
+ <!-- Define some log observers to report on the load test. -->
+ <key>observers</key>
+ <array>
+ <!-- ReportStatistics generates an end-of-run summary of the HTTP requests
+ made, their timings, and their results. -->
+ <string>contrib.performance.loadtest.population.ReportStatistics</string>
- <!-- OperationLogger generates an end-of-run summary of the gross
- operations performed (logical operations which may span more than
- one HTTP request, such as inviting an attendee to an event). -->
- <string>contrib.performance.loadtest.profiles.OperationLogger</string>
- </array>
+ <!-- RequestLogger generates a realtime log of all HTTP requests made
+ during the load test. -->
+ <string>contrib.performance.loadtest.ical.RequestLogger</string>
- </dict>
+ <!-- OperationLogger generates an end-of-run summary of the gross operations
+ performed (logical operations which may span more than one HTTP request,
+ such as inviting an attendee to an event). -->
+ <string>contrib.performance.loadtest.profiles.OperationLogger</string>
+ </array>
+ </dict>
</plist>
Modified: CalendarServer/trunk/contrib/performance/loadtest/population.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/population.py 2011-07-19 21:38:22 UTC (rev 7805)
+++ CalendarServer/trunk/contrib/performance/loadtest/population.py 2011-07-20 03:12:26 UTC (rev 7806)
@@ -129,7 +129,7 @@
def _cycle(self, elements):
while True:
for (weight, value) in elements:
- for i in range(weight):
+ for _ignore_i in range(weight):
yield value
@@ -191,9 +191,9 @@
def add(self, numClients):
- for n in range(numClients):
+ for _ignore_n in range(numClients):
number = self._nextUserNumber()
- user, auth = self._createUser(number)
+ _ignore_user, auth = self._createUser(number)
clientType = self._pop.next()
reactor = loggedReactor(self.reactor)
@@ -203,8 +203,10 @@
d.addErrback(self._clientFailure, reactor)
for profileType in clientType.profileTypes:
- d = profileType(reactor, self, client, number).run()
- d.addErrback(self._profileFailure, profileType, reactor)
+ profile = profileType(reactor, self, client, number)
+ if profile.enabled:
+ d = profile.run()
+ d.addErrback(self._profileFailure, profileType, reactor)
msg(type="status", clientCount=self._user - 1)
@@ -323,7 +325,7 @@
self.printMiscellaneous({'users': self.countUsers()})
self.printHeader([
(label, width)
- for (label, width, fmt)
+ for (label, width, _ignore_fmt)
in self._fields])
self.printData(
[fmt for (label, width, fmt) in self._fields],
Modified: CalendarServer/trunk/contrib/performance/loadtest/profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/profiles.py 2011-07-19 21:38:22 UTC (rev 7805)
+++ CalendarServer/trunk/contrib/performance/loadtest/profiles.py 2011-07-20 03:12:26 UTC (rev 7806)
@@ -134,9 +134,13 @@
"""
A Calendar user who invites and de-invites other users to events.
"""
- def setParameters(self,
- sendInvitationDistribution=NormalDistribution(600, 60),
- inviteeDistanceDistribution=UniformDiscreteDistribution(range(-10, 11))):
+ def setParameters(
+ self,
+ enabled=True,
+ sendInvitationDistribution=NormalDistribution(600, 60),
+ inviteeDistanceDistribution=UniformDiscreteDistribution(range(-10, 11))
+ ):
+ self.enabled = enabled
self._sendInvitationDistribution = sendInvitationDistribution
self._inviteeDistanceDistribution = inviteeDistanceDistribution
@@ -244,7 +248,12 @@
A Calendar user who accepts invitations to events. As well as accepting requests, this
will also remove cancels and replies.
"""
- def setParameters(self, acceptDelayDistribution=NormalDistribution(1200, 60)):
+ def setParameters(
+ self,
+ enabled=True,
+ acceptDelayDistribution=NormalDistribution(1200, 60)
+ ):
+ self.enabled = enabled
self._accepting = set()
self._acceptDelayDistribution = acceptDelayDistribution
@@ -430,13 +439,19 @@
END:VEVENT
END:VCALENDAR
"""))[0]
+
def setParameters(
- self, interval=25,
+ self,
+ enabled=True,
+ interval=25,
eventStartDistribution=NearFutureDistribution(),
eventDurationDistribution=UniformDiscreteDistribution([
- 15 * 60, 30 * 60,
- 45 * 60, 60 * 60,
- 120 * 60])):
+ 15 * 60, 30 * 60,
+ 45 * 60, 60 * 60,
+ 120 * 60
+ ])
+ ):
+ self.enabled = enabled
self._interval = interval
self._eventStartDistribution = eventStartDistribution
self._eventDurationDistribution = eventDurationDistribution
Modified: CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py 2011-07-19 21:38:22 UTC (rev 7805)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py 2011-07-20 03:12:26 UTC (rev 7806)
@@ -277,6 +277,16 @@
return vevent, event, calendar, client
+ def test_enabled(self):
+ userNumber = 13
+ client = StubClient(userNumber)
+
+ inviter = Inviter(None, self.sim, client, userNumber, **{"enabled":False})
+ self.assertEqual(inviter.enabled, False)
+
+ inviter = Inviter(None, self.sim, client, userNumber, **{"enabled":True})
+ self.assertEqual(inviter.enabled, True)
+
def test_doNotAddAttendeeToInbox(self):
"""
When the only calendar with any events is a schedule inbox, no
@@ -445,6 +455,16 @@
AnyUser(), Populator(None), None, None, None)
+ def test_enabled(self):
+ userNumber = 13
+ client = StubClient(userNumber)
+
+ accepter = Accepter(None, self.sim, client, userNumber, **{"enabled":False})
+ self.assertEqual(accepter.enabled, False)
+
+ accepter = Accepter(None, self.sim, client, userNumber, **{"enabled":True})
+ self.assertEqual(accepter.enabled, True)
+
def test_ignoreEventOnUnknownCalendar(self):
"""
If an event on an unknown calendar changes, it is ignored.
@@ -547,7 +567,7 @@
client._setEvent(inboxEvent.url, inboxEvent)
accepter = Accepter(clock, self.sim, client, userNumber)
- accepter.setParameters(Deterministic(randomDelay))
+ accepter.setParameters(acceptDelayDistribution=Deterministic(randomDelay))
accepter.eventChanged(event.url)
clock.advance(randomDelay)
@@ -583,7 +603,7 @@
event = Event(calendarURL + u'1234.ics', None, vevent)
client._events[event.url] = event
accepter = Accepter(clock, self.sim, client, userNumber)
- accepter.setParameters(Deterministic(randomDelay))
+ accepter.setParameters(acceptDelayDistribution=Deterministic(randomDelay))
accepter.eventChanged(event.url)
clock.advance(randomDelay)
@@ -626,7 +646,7 @@
client._setEvent(event.url, event)
accepter = Accepter(clock, self.sim, client, userNumber)
- accepter.setParameters(Deterministic(randomDelay))
+ accepter.setParameters(acceptDelayDistribution=Deterministic(randomDelay))
client.rescheduled.add(event.url)
@@ -647,6 +667,16 @@
AnyUser(), Populator(None), None, None, None)
+ def test_enabled(self):
+ userNumber = 13
+ client = StubClient(userNumber)
+
+ eventer = Eventer(None, self.sim, client, None, **{"enabled":False})
+ self.assertEqual(eventer.enabled, False)
+
+ eventer = Eventer(None, self.sim, client, None, **{"enabled":True})
+ self.assertEqual(eventer.enabled, True)
+
def test_doNotAddEventOnInbox(self):
"""
When the only calendar is a schedule inbox, no attempt is made
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110719/beb8fe18/attachment-0001.html>
More information about the calendarserver-changes
mailing list