[CalendarServer-changes] [7547] CalendarServer/trunk/contrib/performance
source_changes at macosforge.org
source_changes at macosforge.org
Thu Jun 2 08:51:46 PDT 2011
Revision: 7547
http://trac.macosforge.org/projects/calendarserver/changeset/7547
Author: exarkun at twistedmatrix.com
Date: 2011-06-02 08:51:45 -0700 (Thu, 02 Jun 2011)
Log Message:
-----------
Add some support for specifying distributions of values instead of values; use it in the event creator to spread events out (unrealistically) over time.
Modified Paths:
--------------
CalendarServer/trunk/contrib/performance/loadtest/profiles.py
CalendarServer/trunk/contrib/performance/loadtest/sim.py
CalendarServer/trunk/contrib/performance/loadtest/test_sim.py
CalendarServer/trunk/contrib/performance/stats.py
CalendarServer/trunk/contrib/performance/test_stats.py
Modified: CalendarServer/trunk/contrib/performance/loadtest/profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/profiles.py 2011-06-01 14:24:23 UTC (rev 7546)
+++ CalendarServer/trunk/contrib/performance/loadtest/profiles.py 2011-06-02 15:51:45 UTC (rev 7547)
@@ -37,7 +37,7 @@
from twisted.internet.task import LoopingCall
from twisted.web.http import PRECONDITION_FAILED
-from stats import mean, median
+from stats import NearFutureDistribution, UniformDiscreteDistribution, mean, median
from loadtest.logger import SummarizingMixin
from loadtest.ical import IncorrectResponseCode
@@ -340,8 +340,16 @@
END:VEVENT
END:VCALENDAR
"""))[0]
- def setParameters(self, interval=25):
+ def setParameters(
+ self, interval=25,
+ eventStartDistribution=NearFutureDistribution(),
+ eventDurationDistribution=UniformDiscreteDistribution([
+ 15 * 60, 30 * 60,
+ 45 * 60, 60 * 60,
+ 120 * 60])):
self._interval = interval
+ self._eventStartDistribution = eventStartDistribution
+ self._eventDurationDistribution = eventDurationDistribution
def run(self):
@@ -363,12 +371,16 @@
vevent = vcalendar.contents[u'vevent'][0]
tz = vevent.contents[u'created'][0].value.tzinfo
dtstamp = datetime.now(tz)
+ dtstart = datetime.fromtimestamp(self._eventStartDistribution.sample(), tz)
+ dtend = dtstart + timedelta(seconds=self._eventDurationDistribution.sample())
vevent.contents[u'created'][0].value = dtstamp
vevent.contents[u'dtstamp'][0].value = dtstamp
- vevent.contents[u'dtstart'][0].value = dtstamp
- vevent.contents[u'dtend'][0].value = dtstamp + timedelta(hours=1)
+ vevent.contents[u'dtstart'][0].value = dtstart
+ vevent.contents[u'dtend'][0].value = dtend
vevent.contents[u'uid'][0].value = unicode(uuid4())
+ print 'Creating event from', dtstart, 'to', dtend
+
href = '%s%s.ics' % (
calendar.url, vevent.contents[u'uid'][0].value)
d = self._client.addEvent(href, vcalendar)
Modified: CalendarServer/trunk/contrib/performance/loadtest/sim.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/sim.py 2011-06-01 14:24:23 UTC (rev 7546)
+++ CalendarServer/trunk/contrib/performance/loadtest/sim.py 2011-06-02 15:51:45 UTC (rev 7547)
@@ -197,7 +197,7 @@
namedAny(clientConfig["software"]),
[ProfileType(
namedAny(profile["class"]),
- profile["params"])
+ cls._convertParams(profile["params"]))
for profile in clientConfig["profiles"]]))
if not parameters.clients:
parameters.addClient(
@@ -217,8 +217,32 @@
return cls(server, arrival, parameters,
observers=observers, records=records)
+ @classmethod
+ def _convertParams(cls, params):
+ """
+ Find parameter values which should be more structured than plistlib is
+ capable of constructing and replace them with the more structured form.
+ Specifically, find keys that end with C{"Distribution"} and convert
+ them into some kind of distribution object using the associated
+ dictionary of keyword arguments.
+ """
+ for k, v in params.iteritems():
+ if k.endswith('Distribution'):
+ params[k] = cls._convertDistribution(v)
+ return params
+
+
@classmethod
+ def _convertDistribution(cls, value):
+ """
+ Construct and return a new distribution object using the type and
+ params specified by C{value}.
+ """
+ return namedAny(value['type'])(**value['params'])
+
+
+ @classmethod
def main(cls, args=None):
simulator = cls.fromCommandLine(args)
raise SystemExit(simulator.run())
Modified: CalendarServer/trunk/contrib/performance/loadtest/test_sim.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_sim.py 2011-06-01 14:24:23 UTC (rev 7546)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_sim.py 2011-06-02 15:51:45 UTC (rev 7547)
@@ -30,6 +30,7 @@
from twistedcaldav.directory.idirectory import IDirectoryService
from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
+from stats import NormalDistribution
from loadtest.ical import SnowLeopard
from loadtest.profiles import Eventer, Inviter, Accepter
from loadtest.population import (
@@ -306,13 +307,25 @@
config.setContent(writePlistToString({
"clients": [{
"software": "loadtest.ical.SnowLeopard",
- "profiles": [{"class": "loadtest.profiles.Eventer", "params": {"interval": 25}}],
+ "profiles": [{
+ "params": {
+ "interval": 25,
+ "eventStartDistribution": {
+ "type": "stats.NormalDistribution",
+ "params": {
+ "mu": 123,
+ "sigma": 456,
+ }}},
+ "class": "loadtest.profiles.Eventer"}],
"weight": 3,
}]}))
+
sim = LoadSimulator.fromCommandLine(['--config', config.path])
expectedParameters = PopulationParameters()
expectedParameters.addClient(
- 3, ClientType(SnowLeopard, [ProfileType(Eventer, {"interval": 25})]))
+ 3, ClientType(SnowLeopard, [ProfileType(Eventer, {
+ "interval": 25,
+ "eventStartDistribution": NormalDistribution(123, 456)})]))
self.assertEquals(sim.parameters, expectedParameters)
Modified: CalendarServer/trunk/contrib/performance/stats.py
===================================================================
--- CalendarServer/trunk/contrib/performance/stats.py 2011-06-01 14:24:23 UTC (rev 7546)
+++ CalendarServer/trunk/contrib/performance/stats.py 2011-06-02 15:51:45 UTC (rev 7547)
@@ -14,6 +14,12 @@
# limitations under the License.
##
+import random, time
+
+from zope.interface import Interface, implements
+
+from twisted.python.util import FancyEqMixin
+
import sqlparse
NANO = 1000000000.0
@@ -210,3 +216,74 @@
"""
buckets = {}
return []
+
+
+class IPopulation(Interface):
+ def sample():
+ pass
+
+
+class UniformDiscreteDistribution(object, FancyEqMixin):
+ """
+
+ """
+ implements(IPopulation)
+
+ compareAttributes = ['_values']
+
+ def __init__(self, values):
+ self._values = values
+ self._refill()
+
+
+ def _refill(self):
+ self._remaining = self._values[:]
+ random.shuffle(self._remaining)
+
+
+ def sample(self):
+ if not self._remaining:
+ self._refill()
+ return self._remaining.pop()
+
+
+
+class LogNormalDistribution(object, FancyEqMixin):
+ """
+ """
+ implements(IPopulation)
+
+ compareAttributes = ['_mu', '_sigma']
+
+ def __init__(self, mu, sigma):
+ self._mu = mu
+ self._sigma = sigma
+
+
+ def sample(self):
+ return random.lognormvariate(self._mu, self._sigma)
+
+
+
+class NearFutureDistribution(object, FancyEqMixin):
+ compareAttributes = ['_offset']
+
+ def __init__(self):
+ self._offset = LogNormalDistribution(7, 0.8)
+
+
+ def sample(self):
+ return time.time() + self._offset.sample()
+
+
+
+class NormalDistribution(object, FancyEqMixin):
+ compareAttributes = ['_mu', '_sigma']
+
+ def __init__(self, mu, sigma):
+ self._mu = mu
+ self._sigma = sigma
+
+
+ def sample(self):
+ return random.normalvariate(self._mu, self._sigma)
Modified: CalendarServer/trunk/contrib/performance/test_stats.py
===================================================================
--- CalendarServer/trunk/contrib/performance/test_stats.py 2011-06-01 14:24:23 UTC (rev 7546)
+++ CalendarServer/trunk/contrib/performance/test_stats.py 2011-06-02 15:51:45 UTC (rev 7547)
@@ -16,7 +16,7 @@
from twisted.trial.unittest import TestCase
-from stats import SQLDuration, quantize
+from stats import SQLDuration, LogNormalDistribution, UniformDiscreteDistribution, quantize
class SQLDurationTests(TestCase):
def setUp(self):
@@ -41,6 +41,27 @@
'SELECT foo FROM bar WHERE ?')
+
+class DistributionTests(TestCase):
+ def test_lognormal(self):
+ dist = LogNormalDistribution(1, 1)
+ for i in range(100):
+ value = dist.sample()
+ self.assertIsInstance(value, float)
+ self.assertTrue(value >= 0.0, "negative value %r" % (value,))
+ self.assertTrue(value <= 1000, "implausibly high value %r" % (value,))
+
+
+ def test_uniformdiscrete(self):
+ population = [1, 5, 6, 9]
+ counts = dict.fromkeys(population, 0)
+ dist = UniformDiscreteDistribution(population)
+ for i in range(len(population) * 10):
+ counts[dist.sample()] += 1
+ self.assertEqual(dict.fromkeys(population, 10), counts)
+
+
+
class QuantizationTests(TestCase):
"""
Tests for L{quantize} which constructs discrete datasets of
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110602/018c584e/attachment-0001.html>
More information about the calendarserver-changes
mailing list