[CalendarServer-changes] [6094] CalendarServer/trunk/contrib/performance
source_changes at macosforge.org
source_changes at macosforge.org
Tue Aug 17 11:50:36 PDT 2010
Revision: 6094
http://trac.macosforge.org/projects/calendarserver/changeset/6094
Author: exarkun at twistedmatrix.com
Date: 2010-08-17 11:50:35 -0700 (Tue, 17 Aug 2010)
Log Message:
-----------
Split things up into a more coherent module layout; primary benefit is that pickles are now loadable
Modified Paths:
--------------
CalendarServer/trunk/contrib/performance/benchmark.sh
Added Paths:
-----------
CalendarServer/trunk/contrib/performance/benchmark
CalendarServer/trunk/contrib/performance/benchmark.py
CalendarServer/trunk/contrib/performance/stats.py
CalendarServer/trunk/contrib/performance/vfreebusy.py
Removed Paths:
-------------
CalendarServer/trunk/contrib/performance/mkcal.py
Added: CalendarServer/trunk/contrib/performance/benchmark
===================================================================
--- CalendarServer/trunk/contrib/performance/benchmark (rev 0)
+++ CalendarServer/trunk/contrib/performance/benchmark 2010-08-17 18:50:35 UTC (rev 6094)
@@ -0,0 +1,4 @@
+#!/usr/bin/python
+
+from benchmark import main
+main()
Property changes on: CalendarServer/trunk/contrib/performance/benchmark
___________________________________________________________________
Added: svn:executable
+ *
Added: CalendarServer/trunk/contrib/performance/benchmark.py
===================================================================
--- CalendarServer/trunk/contrib/performance/benchmark.py (rev 0)
+++ CalendarServer/trunk/contrib/performance/benchmark.py 2010-08-17 18:50:35 UTC (rev 6094)
@@ -0,0 +1,208 @@
+import sys
+
+from signal import SIGINT
+from pickle import dump
+
+from datetime import datetime
+from StringIO import StringIO
+
+from twisted.internet.protocol import ProcessProtocol
+from twisted.internet.defer import (
+ Deferred, inlineCallbacks, gatherResults)
+from twisted.internet import reactor
+
+from stats import SQLDuration, Bytes
+import vfreebusy
+
+
+class DTraceBug(Exception):
+ """
+ Represents some kind of problem related to a shortcoming in dtrace
+ itself.
+ """
+
+
+
+class IOMeasureConsumer(ProcessProtocol):
+ def __init__(self, started, done):
+ self.started = started
+ self.done = done
+
+
+ def connectionMade(self):
+ self.out = StringIO()
+ self._out = ''
+ self._err = ''
+
+
+ def errReceived(self, bytes):
+ self._err += bytes
+ if 'Interrupted system call' in self._err:
+ started = self.started
+ self.started = None
+ started.errback(DTraceBug(self._err))
+
+
+ def outReceived(self, bytes):
+ if self.started is None:
+ self.out.write(bytes)
+ else:
+ self._out += bytes
+ if self._out == 'READY\n':
+ started = self.started
+ self.started = None
+ started.callback(None)
+
+
+ def processEnded(self, reason):
+ if self.started is None:
+ self.done.callback(self.out.getvalue())
+ else:
+ self.started.errback(RuntimeError("Exited too soon"))
+
+
+class DTraceCollector(object):
+ def __init__(self, pids):
+ self.pids = pids
+ self._read = []
+ self._write = []
+ self._execute = []
+ self._iternext = []
+
+
+ def stats(self):
+ return {
+ Bytes('read'): self._read,
+ Bytes('write'): self._write,
+ SQLDuration('execute'): self._execute,
+ SQLDuration('iternext'): self._iternext,
+ }
+
+
+ def _parse(self, dtrace):
+ file('dtrace.log', 'a').write(dtrace)
+
+ self.sql = self.start = None
+ for L in dtrace.split('\n\1'):
+
+ # dtrace puts some extra newlines in the output sometimes. Get rid of them.
+ L = L.strip()
+ if not L:
+ continue
+
+ op, rest = L.split(None, 1)
+ getattr(self, '_op_' + op)(op, rest)
+ self.sql = self.start = None
+
+
+ def _op_EXECUTE(self, cmd, rest):
+ which, when = rest.split(None, 1)
+ if which == 'SQL':
+ self.sql = when
+ return
+
+ when = int(when)
+ if which == 'ENTRY':
+ self.start = when
+ elif which == 'RETURN':
+ if self.start is None:
+ print 'return without entry at', when, 'in', cmd
+ else:
+ diff = when - self.start
+ if diff < 0:
+ print 'Completely bogus EXECUTE', self.start, when
+ else:
+ if cmd == 'EXECUTE':
+ accum = self._execute
+ elif cmd == 'ITERNEXT':
+ accum = self._iternext
+
+ accum.append((self.sql, diff))
+ self.start = None
+
+ _op_ITERNEXT = _op_EXECUTE
+
+ def _op_B_READ(self, cmd, rest):
+ self._read.append(int(rest))
+
+
+ def _op_B_WRITE(self, cmd, rest):
+ self._write.append(int(rest))
+
+
+ def start(self):
+ ready = []
+ self.finished = []
+ self.dtraces = {}
+ for p in self.pids:
+ started, stopped = self._startDTrace(p)
+ ready.append(started)
+ self.finished.append(stopped)
+ return gatherResults(ready)
+
+
+ def _startDTrace(self, pid):
+ started = Deferred()
+ stopped = Deferred()
+ self.dtraces[pid] = reactor.spawnProcess(
+ IOMeasureConsumer(started, stopped),
+ "/usr/sbin/dtrace",
+ ["/usr/sbin/dtrace", "-q", "-p", str(pid), "-s",
+ "io_measure.d"])
+ def eintr(reason):
+ reason.trap(DTraceBug)
+ print 'Dtrace startup failed (', reason.getErrorMessage(), '), retrying.'
+ return self._startDTrace(pid)
+ started.addErrback(eintr)
+ stopped.addCallback(self._cleanup, pid)
+ stopped.addCallback(self._parse)
+ return started, stopped
+
+
+ def _cleanup(self, passthrough, pid):
+ del self.dtraces[pid]
+ return passthrough
+
+
+ def stop(self):
+ for proc in self.dtraces.itervalues():
+ proc.signalProcess(SIGINT)
+ d = gatherResults(self.finished)
+ d.addCallback(lambda ign: self.stats())
+ return d
+
+
+
+ at inlineCallbacks
+def benchmark(argv):
+ # Figure out which pids we are benchmarking.
+ pids = map(int, argv)
+
+ parameters = [1, 10, 100]
+ stuff = [
+ ('vfreebusy', vfreebusy.measure, parameters),
+ ]
+
+ statistics = {}
+
+ for stat, survey, parameter in stuff:
+ print 'Surveying', stat
+ for p in parameter:
+ print 'Parameter at', p
+ dtrace = DTraceCollector(pids)
+ data = yield survey(dtrace, p, 100)
+ statistics[stat] = data
+
+ fObj = file(datetime.now().isoformat(), 'w')
+ dump(statistics, fObj, 2)
+ fObj.close()
+
+
+def main():
+ from twisted.python.log import err
+ from twisted.python.failure import startDebugMode
+ startDebugMode()
+ d = benchmark(sys.argv[1:])
+ d.addErrback(err)
+ d.addCallback(lambda ign: reactor.stop())
+ reactor.run()
Modified: CalendarServer/trunk/contrib/performance/benchmark.sh
===================================================================
--- CalendarServer/trunk/contrib/performance/benchmark.sh 2010-08-17 18:34:36 UTC (rev 6093)
+++ CalendarServer/trunk/contrib/performance/benchmark.sh 2010-08-17 18:50:35 UTC (rev 6094)
@@ -1,3 +1,2 @@
#!/bin/bash
-sudo PYTHONPATH=$PYTHONPATH:../../../vobject/:../../../../CalDAVClientLibrary/trunk/src:../../../Twisted/ python -i mkcal.py "$@"
-
+sudo PYTHONPATH=$PYTHONPATH:../../../vobject/:../../../../CalDAVClientLibrary/trunk/src:../../../Twisted/ ./benchmark "$@"
Deleted: CalendarServer/trunk/contrib/performance/mkcal.py
===================================================================
--- CalendarServer/trunk/contrib/performance/mkcal.py 2010-08-17 18:34:36 UTC (rev 6093)
+++ CalendarServer/trunk/contrib/performance/mkcal.py 2010-08-17 18:50:35 UTC (rev 6094)
@@ -1,438 +0,0 @@
-"""
-Make a new calendar using an existing user account on an already-running iCal
-server.
-"""
-
-import sys
-
-from subprocess import PIPE, Popen
-from signal import SIGINT
-from pickle import dump
-
-from urllib2 import HTTPDigestAuthHandler
-from uuid import uuid1, uuid4
-from datetime import datetime, date, timedelta
-from time import time
-from StringIO import StringIO
-
-import vobject
-
-from client.account import CalDAVAccount
-from protocol.url import URL
-
-from zope.interface import implements
-
-from twisted.internet.protocol import ProcessProtocol, Protocol
-from twisted.internet.defer import (
- Deferred, inlineCallbacks, returnValue, succeed, gatherResults)
-from twisted.internet import reactor
-from twisted.web.iweb import IBodyProducer
-from twisted.web.client import Agent
-from twisted.web.http_headers import Headers
-
-from httpauth import AuthHandlerAgent
-
-
-# XXX Represent these as vobjects? Would make it easier to add more vevents.
-event = """\
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-END:VTIMEZONE
-%(VEVENTS)s\
-END:VCALENDAR
-"""
-
-vfreebusy = """\
-BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-METHOD:REQUEST
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VFREEBUSY
-UID:81F582C8-4E7F-491C-85F4-E541864BE0FA
-DTEND:20100730T150000Z
-ATTENDEE:urn:uuid:user02
-DTSTART:20100730T140000Z
-X-CALENDARSERVER-MASK-UID:EC75A61B-08A3-44FD-BFBB-2457BBD0D490
-DTSTAMP:20100729T174751Z
-ORGANIZER:mailto:user01 at example.com
-SUMMARY:Availability for urn:uuid:user02
-END:VFREEBUSY
-END:VCALENDAR
-"""
-
-def formatDate(d):
- return ''.join(filter(str.isalnum, d.isoformat()))
-
-def makeEvent(i):
- s = """\
-BEGIN:VEVENT
-UID:%(UID)s
-DTSTART;TZID=America/New_York:%(START)s
-DTEND;TZID=America/New_York:%(END)s
-CREATED:20100729T193912Z
-DTSTAMP:20100729T195557Z
-SEQUENCE:%(SEQUENCE)s
-SUMMARY:STUFF IS THINGS
-TRANSP:OPAQUE
-END:VEVENT
-"""
- base = datetime(2010, 7, 30, 11, 15, 00)
- interval = timedelta(0, 5)
- duration = timedelta(0, 3)
- return event % {
- 'VEVENTS': s % {
- 'UID': uuid4(),
- 'START': formatDate(base + i * interval),
- 'END': formatDate(base + i * interval + duration),
- 'SEQUENCE': i,
- },
- }
-
-
-def makeEvents(n):
- return [makeEvent(i) for i in range(n)]
-
-
-class _DiscardReader(Protocol):
- def __init__(self, finished):
- self.finished = finished
-
-
- def dataReceived(self, bytes):
- pass
-
-
- def connectionLost(self, reason):
- self.finished.callback(None)
-
-
-
-def readBody(response):
- finished = Deferred()
- response.deliverBody(_DiscardReader(finished))
- return finished
-
-
-
-class StringProducer(object):
- implements(IBodyProducer)
-
- def __init__(self, body):
- self._body = body
- self.length = len(self._body)
-
-
- def startProducing(self, consumer):
- consumer.write(self._body)
- return succeed(None)
-
-
- at inlineCallbacks
-def measure(dtrace, events, samples):
- # First set things up
- account = CalDAVAccount(
- "localhost:8008", user="user01", pswd="user01", root="/", principal="/")
- account.session.deleteResource(
- URL("/calendars/users/user01/monkeys3/"))
- account.session.makeCalendar(
- URL("/calendars/users/user01/monkeys3/"))
-
- for i, cal in enumerate(makeEvents(events)):
- account.session.writeData(
- URL("/calendars/users/user01/monkeys3/foo-%d.ics" % (i,)),
- cal,
- "text/calendar")
-
- # CalDAVClientLibrary can't seem to POST things.
- authinfo = HTTPDigestAuthHandler()
- authinfo.add_password(
- realm="Test Realm",
- uri="http://localhost:8008/",
- user="user01",
- passwd="user01")
-
- agent = AuthHandlerAgent(Agent(reactor), authinfo)
- method = 'POST'
- uri = 'http://localhost:8008/calendars/__uids__/user01/outbox/'
- headers = Headers({"content-type": ["text/calendar"]})
- body = StringProducer(vfreebusy)
-
- # Now sample it a bunch of times
- data = []
- yield dtrace.start()
- for i in range(samples):
- before = time()
- response = yield agent.request(
- method, uri, headers, body)
- yield readBody(response)
- after = time()
- data.append(after - before)
- stats = yield dtrace.stop()
- stats[Duration('urlopen time')] = data
- returnValue(stats)
-
-
-class _Statistic(object):
- def __init__(self, name):
- self.name = name
-
-
- def summarize(self, data):
- print self.name, 'mean', mean(data)
- print self.name, 'median', median(data)
- print self.name, 'stddev', stddev(data)
- print self.name, 'sum', sum(data)
-
-
- def write(self, basename, data):
- fObj = file(basename % (self.name,), 'w')
- fObj.write('\n'.join(map(str, data)) + '\n')
- fObj.close()
-
-
-
-class Duration(_Statistic):
- pass
-
-
-class SQLDuration(_Statistic):
- def summarize(self, data):
- statements = {}
- intervals = []
- for (sql, interval) in data:
- intervals.append(interval)
- statements[sql] = statements.get(sql, 0) + 1
- for statement, count in statements.iteritems():
- print count, ':', statement.replace('\n', ' ')
- return _Statistic.summarize(self, intervals)
-
-
-class Bytes(_Statistic):
- pass
-
-
-
-class DTraceBug(Exception):
- """
- Represents some kind of problem related to a shortcoming in dtrace
- itself.
- """
-
-
-
-class DTraceCollector(object):
- def __init__(self, pids):
- self.pids = pids
- self._read = []
- self._write = []
- self._execute = []
- self._iternext = []
-
-
- def stats(self):
- return {
- Bytes('read'): self._read,
- Bytes('write'): self._write,
- SQLDuration('execute'): self._execute,
- SQLDuration('iternext'): self._iternext,
- }
-
-
- def _parse(self, dtrace):
- file('dtrace.log', 'a').write(dtrace)
-
- self.sql = self.start = None
- for L in dtrace.split('\n\1'):
-
- # dtrace puts some extra newlines in the output sometimes. Get rid of them.
- L = L.strip()
- if not L:
- continue
-
- op, rest = L.split(None, 1)
- getattr(self, '_op_' + op)(op, rest)
- self.sql = self.start = None
-
-
- def _op_EXECUTE(self, cmd, rest):
- which, when = rest.split(None, 1)
- if which == 'SQL':
- self.sql = when
- return
-
- when = int(when)
- if which == 'ENTRY':
- self.start = when
- elif which == 'RETURN':
- if self.start is None:
- print 'return without entry at', when, 'in', cmd
- else:
- diff = when - self.start
- if diff < 0:
- print 'Completely bogus EXECUTE', self.start, when
- else:
- if cmd == 'EXECUTE':
- accum = self._execute
- elif cmd == 'ITERNEXT':
- accum = self._iternext
-
- accum.append((self.sql, diff))
- self.start = None
-
- _op_ITERNEXT = _op_EXECUTE
-
- def _op_B_READ(self, cmd, rest):
- self._read.append(int(rest))
-
-
- def _op_B_WRITE(self, cmd, rest):
- self._write.append(int(rest))
-
-
- def start(self):
- ready = []
- self.finished = []
- self.dtraces = {}
- for p in self.pids:
- started, stopped = self._startDTrace(p)
- ready.append(started)
- self.finished.append(stopped)
- return gatherResults(ready)
-
-
- def _startDTrace(self, pid):
- started = Deferred()
- stopped = Deferred()
- self.dtraces[pid] = reactor.spawnProcess(
- IOMeasureConsumer(started, stopped),
- "/usr/sbin/dtrace",
- ["/usr/sbin/dtrace", "-q", "-p", str(pid), "-s",
- "io_measure.d"])
- def eintr(reason):
- reason.trap(DTraceBug)
- print 'Dtrace startup failed (', reason.getErrorMessage(), '), retrying.'
- return self._startDTrace(pid)
- started.addErrback(eintr)
- stopped.addCallback(self._cleanup, pid)
- stopped.addCallback(self._parse)
- return started, stopped
-
-
- def _cleanup(self, passthrough, pid):
- del self.dtraces[pid]
- return passthrough
-
-
- def stop(self):
- for proc in self.dtraces.itervalues():
- proc.signalProcess(SIGINT)
- d = gatherResults(self.finished)
- d.addCallback(lambda ign: self.stats())
- return d
-
-
-
-class IOMeasureConsumer(ProcessProtocol):
- def __init__(self, started, done):
- self.started = started
- self.done = done
-
-
- def connectionMade(self):
- self.out = StringIO()
- self._out = ''
- self._err = ''
-
-
- def errReceived(self, bytes):
- self._err += bytes
- if 'Interrupted system call' in self._err:
- started = self.started
- self.started = None
- started.errback(DTraceBug(self._err))
-
-
- def outReceived(self, bytes):
- if self.started is None:
- self.out.write(bytes)
- else:
- self._out += bytes
- if self._out == 'READY\n':
- started = self.started
- self.started = None
- started.callback(None)
-
-
- def processEnded(self, reason):
- if self.started is None:
- self.done.callback(self.out.getvalue())
- else:
- self.started.errback(RuntimeError("Exited too soon"))
-
-
-def mean(samples):
- return sum(samples) / len(samples)
-
-
-def median(samples):
- return sorted(samples)[len(samples) / 2]
-
-
-def stddev(samples):
- m = mean(samples)
- variance = sum([(datum - m) ** 2 for datum in samples]) / len(samples)
- return variance ** 0.5
-
-
- at inlineCallbacks
-def main():
- # Figure out which pids we are benchmarking.
- pids = map(int, sys.argv[1:])
-
- parameters = [1, 10, 100]
- stuff = [
- ('vfreebusy', measure, parameters),
- ]
-
- statistics = {}
-
- for stat, survey, parameter in stuff:
- print 'Surveying', stat
- for p in parameter:
- print 'Parameter at', p
- dtrace = DTraceCollector(pids)
- data = yield survey(dtrace, p, 100)
- statistics[stat] = data
-
- fObj = file(datetime.now().isoformat(), 'w')
- dump(statistics, fObj, 2)
- fObj.close()
-
-
-if __name__ == '__main__':
- from twisted.python.log import err
- from twisted.python.failure import startDebugMode
- startDebugMode()
- d = main()
- d.addErrback(err)
- d.addCallback(lambda ign: reactor.stop())
- reactor.run()
Added: CalendarServer/trunk/contrib/performance/stats.py
===================================================================
--- CalendarServer/trunk/contrib/performance/stats.py (rev 0)
+++ CalendarServer/trunk/contrib/performance/stats.py 2010-08-17 18:50:35 UTC (rev 6094)
@@ -0,0 +1,58 @@
+
+
+def mean(samples):
+ return sum(samples) / len(samples)
+
+
+def median(samples):
+ return sorted(samples)[len(samples) / 2]
+
+
+def stddev(samples):
+ m = mean(samples)
+ variance = sum([(datum - m) ** 2 for datum in samples]) / len(samples)
+ return variance ** 0.5
+
+
+class _Statistic(object):
+ def __init__(self, name):
+ self.name = name
+
+
+ def summarize(self, data):
+ print self.name, 'mean', mean(data)
+ print self.name, 'median', median(data)
+ print self.name, 'stddev', stddev(data)
+ print self.name, 'sum', sum(data)
+
+
+ def write(self, basename, data):
+ fObj = file(basename % (self.name,), 'w')
+ fObj.write('\n'.join(map(str, data)) + '\n')
+ fObj.close()
+
+
+
+class Duration(_Statistic):
+ pass
+
+
+
+class SQLDuration(_Statistic):
+ def summarize(self, data):
+ statements = {}
+ intervals = []
+ for (sql, interval) in data:
+ intervals.append(interval)
+ statements[sql] = statements.get(sql, 0) + 1
+ for statement, count in statements.iteritems():
+ print count, ':', statement.replace('\n', ' ')
+ return _Statistic.summarize(self, intervals)
+
+
+
+class Bytes(_Statistic):
+ pass
+
+
+
Added: CalendarServer/trunk/contrib/performance/vfreebusy.py
===================================================================
--- CalendarServer/trunk/contrib/performance/vfreebusy.py (rev 0)
+++ CalendarServer/trunk/contrib/performance/vfreebusy.py 2010-08-17 18:50:35 UTC (rev 6094)
@@ -0,0 +1,182 @@
+"""
+Benchmark a server's handling of VFREEBUSY requests.
+"""
+
+from urllib2 import HTTPDigestAuthHandler
+from uuid import uuid4
+from datetime import datetime, timedelta
+from time import time
+
+from client.account import CalDAVAccount
+from protocol.url import URL
+
+from zope.interface import implements
+
+from twisted.internet.protocol import Protocol
+from twisted.internet.defer import (
+ Deferred, inlineCallbacks, returnValue, succeed)
+from twisted.internet import reactor
+from twisted.web.iweb import IBodyProducer
+from twisted.web.client import Agent
+from twisted.web.http_headers import Headers
+
+from httpauth import AuthHandlerAgent
+from stats import Duration
+
+
+# XXX Represent these as vobjects? Would make it easier to add more vevents.
+event = """\
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+BEGIN:VTIMEZONE
+TZID:America/New_York
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+END:VTIMEZONE
+%(VEVENTS)s\
+END:VCALENDAR
+"""
+
+vfreebusy = """\
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:2.0
+METHOD:REQUEST
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+BEGIN:VFREEBUSY
+UID:81F582C8-4E7F-491C-85F4-E541864BE0FA
+DTEND:20100730T150000Z
+ATTENDEE:urn:uuid:user02
+DTSTART:20100730T140000Z
+X-CALENDARSERVER-MASK-UID:EC75A61B-08A3-44FD-BFBB-2457BBD0D490
+DTSTAMP:20100729T174751Z
+ORGANIZER:mailto:user01 at example.com
+SUMMARY:Availability for urn:uuid:user02
+END:VFREEBUSY
+END:VCALENDAR
+"""
+
+def formatDate(d):
+ return ''.join(filter(str.isalnum, d.isoformat()))
+
+def makeEvent(i):
+ s = """\
+BEGIN:VEVENT
+UID:%(UID)s
+DTSTART;TZID=America/New_York:%(START)s
+DTEND;TZID=America/New_York:%(END)s
+CREATED:20100729T193912Z
+DTSTAMP:20100729T195557Z
+SEQUENCE:%(SEQUENCE)s
+SUMMARY:STUFF IS THINGS
+TRANSP:OPAQUE
+END:VEVENT
+"""
+ base = datetime(2010, 7, 30, 11, 15, 00)
+ interval = timedelta(0, 5)
+ duration = timedelta(0, 3)
+ return event % {
+ 'VEVENTS': s % {
+ 'UID': uuid4(),
+ 'START': formatDate(base + i * interval),
+ 'END': formatDate(base + i * interval + duration),
+ 'SEQUENCE': i,
+ },
+ }
+
+
+def makeEvents(n):
+ return [makeEvent(i) for i in range(n)]
+
+
+class _DiscardReader(Protocol):
+ def __init__(self, finished):
+ self.finished = finished
+
+
+ def dataReceived(self, bytes):
+ pass
+
+
+ def connectionLost(self, reason):
+ self.finished.callback(None)
+
+
+
+def readBody(response):
+ finished = Deferred()
+ response.deliverBody(_DiscardReader(finished))
+ return finished
+
+
+
+class StringProducer(object):
+ implements(IBodyProducer)
+
+ def __init__(self, body):
+ self._body = body
+ self.length = len(self._body)
+
+
+ def startProducing(self, consumer):
+ consumer.write(self._body)
+ return succeed(None)
+
+
+ at inlineCallbacks
+def measure(dtrace, events, samples):
+ # First set things up
+ account = CalDAVAccount(
+ "localhost:8008", user="user01", pswd="user01", root="/", principal="/")
+ account.session.deleteResource(
+ URL("/calendars/users/user01/monkeys3/"))
+ account.session.makeCalendar(
+ URL("/calendars/users/user01/monkeys3/"))
+
+ for i, cal in enumerate(makeEvents(events)):
+ account.session.writeData(
+ URL("/calendars/users/user01/monkeys3/foo-%d.ics" % (i,)),
+ cal,
+ "text/calendar")
+
+ # CalDAVClientLibrary can't seem to POST things.
+ authinfo = HTTPDigestAuthHandler()
+ authinfo.add_password(
+ realm="Test Realm",
+ uri="http://localhost:8008/",
+ user="user01",
+ passwd="user01")
+
+ agent = AuthHandlerAgent(Agent(reactor), authinfo)
+ method = 'POST'
+ uri = 'http://localhost:8008/calendars/__uids__/user01/outbox/'
+ headers = Headers({"content-type": ["text/calendar"]})
+ body = StringProducer(vfreebusy)
+
+ # Now sample it a bunch of times
+ data = []
+ yield dtrace.start()
+ for i in range(samples):
+ before = time()
+ response = yield agent.request(
+ method, uri, headers, body)
+ yield readBody(response)
+ after = time()
+ data.append(after - before)
+ stats = yield dtrace.stop()
+ stats[Duration('urlopen time')] = data
+ returnValue(stats)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100817/cef0b43e/attachment-0001.html>
More information about the calendarserver-changes
mailing list