[CalendarServer-changes] [9528] CalendarServer/trunk/contrib/performance

source_changes at macosforge.org source_changes at macosforge.org
Mon Aug 6 09:47:35 PDT 2012


Revision: 9528
          http://trac.macosforge.org/projects/calendarserver/changeset/9528
Author:   cdaboo at apple.com
Date:     2012-08-06 09:47:35 -0700 (Mon, 06 Aug 2012)
Log Message:
-----------
New benchmark tool to examine SQL statement count, rows returned and time against various HTTP requests,

Added Paths:
-----------
    CalendarServer/trunk/contrib/performance/sqlusage/
    CalendarServer/trunk/contrib/performance/sqlusage/__init__.py
    CalendarServer/trunk/contrib/performance/sqlusage/requests/
    CalendarServer/trunk/contrib/performance/sqlusage/requests/__init__.py
    CalendarServer/trunk/contrib/performance/sqlusage/requests/httpTests.py
    CalendarServer/trunk/contrib/performance/sqlusage/requests/multiget.py
    CalendarServer/trunk/contrib/performance/sqlusage/requests/propfind.py
    CalendarServer/trunk/contrib/performance/sqlusage/requests/query.py
    CalendarServer/trunk/contrib/performance/sqlusage/requests/sync.py
    CalendarServer/trunk/contrib/performance/sqlusage/sqlusage.py

Added: CalendarServer/trunk/contrib/performance/sqlusage/__init__.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/__init__.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/__init__.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+SQL usage analysis tool.
+"""

Added: CalendarServer/trunk/contrib/performance/sqlusage/requests/__init__.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/requests/__init__.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/requests/__init__.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+SQL usage requests.
+"""

Added: CalendarServer/trunk/contrib/performance/sqlusage/requests/httpTests.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/requests/httpTests.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/requests/httpTests.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,98 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Defines a set of HTTP requests to execute and return results.
+"""
+
+class HTTPTestBase(object):
+    """
+    Base class for an HTTP request that executes and results are returned for.
+    """
+
+    class SQLResults(object):
+        
+        def __init__(self, count, rows, timing):
+            self.count = count
+            self.rows = rows
+            self.timing = timing
+        
+    def __init__(self, label, session, href, logFilePath):
+        """
+        @param label: label used to identify the test
+        @type label: C{str}
+        """
+        self.label = label
+        self.session = session
+        self.baseHref = href
+        self.logFilePath = logFilePath
+        self.result = None
+
+    def execute(self):
+        """
+        Execute the HTTP request and read the results.
+        """
+        
+        self.prepare()
+        self.clearLog()
+        self.doRequest()
+        self.collectResults()
+        self.cleanup()
+        return self.result
+
+    def prepare(self):
+        """
+        Do some setup prior to the real request.
+        """
+        pass
+
+    def clearLog(self):
+        """
+        Clear the server's SQL log file.
+        """
+        open(self.logFilePath, "w").write("")
+
+    def doRequest(self):
+        """
+        Execute the actual HTTP request. Sub-classes override.
+        """
+        raise NotImplementedError
+
+    def collectResults(self):
+        """
+        Parse the server log file to extract the details we need.
+        """
+        
+        def extractInt(line):
+            pos = line.find(": ")
+            return int(line[pos+2:])
+
+        def extractFloat(line):
+            pos = line.find(": ")
+            return float(line[pos+2:])
+
+        data = open(self.logFilePath).read()
+        lines = data.splitlines()
+        count = extractInt(lines[4])
+        rows = extractInt(lines[5])
+        timing = extractFloat(lines[6])
+        self.result = HTTPTestBase.SQLResults(count, rows, timing)
+
+    def cleanup(self):
+        """
+        Do some cleanup after the real request.
+        """
+        pass

Added: CalendarServer/trunk/contrib/performance/sqlusage/requests/multiget.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/requests/multiget.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/requests/multiget.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,56 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from caldavclientlibrary.protocol.caldav.definitions import caldavxml
+from caldavclientlibrary.protocol.caldav.multiget import Multiget
+from caldavclientlibrary.protocol.http.data.string import ResponseDataString
+from caldavclientlibrary.protocol.webdav.definitions import davxml, statuscodes
+from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
+from twext.web2.dav.util import joinURL
+
+class MultigetTest(HTTPTestBase):
+    """
+    A multiget operation
+    """
+
+    def __init__(self, label, session, href, logFilePath, count):
+        super(MultigetTest, self).__init__(label, session, href, logFilePath)
+        self.count = count
+    
+    def doRequest(self):
+        """
+        Execute the actual HTTP request.
+        """
+        hrefs = [joinURL(self.baseHref, "%d.ics" % (i+1,)) for i in range(self.count)]
+        props = (
+            davxml.getetag,
+            caldavxml.calendar_data,
+            caldavxml.schedule_tag,
+        )
+
+        # Create CalDAV multiget
+        request = Multiget(self.session, self.baseHref, hrefs, props)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.session.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+            pass
+        else:
+            raise RuntimeError("Muliget request failed: %s" % (request.getStatusCode(),))

Added: CalendarServer/trunk/contrib/performance/sqlusage/requests/propfind.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/requests/propfind.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/requests/propfind.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,53 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from caldavclientlibrary.protocol.http.data.string import ResponseDataString
+from caldavclientlibrary.protocol.webdav.definitions import davxml, statuscodes,\
+    headers
+from caldavclientlibrary.protocol.webdav.propfind import PropFind
+from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
+
+class PropfindTest(HTTPTestBase):
+    """
+    A propfind operation
+    """
+
+    def __init__(self, label, session, href, logFilePath, depth=1):
+        super(PropfindTest, self).__init__(label, session, href, logFilePath)
+        self.depth = headers.Depth1 if depth == 1 else headers.Depth0
+    
+    def doRequest(self):
+        """
+        Execute the actual HTTP request.
+        """
+        props = (
+            davxml.getetag,
+            davxml.getcontenttype,
+        )
+
+        # Create WebDAV propfind
+        request = PropFind(self.session, self.baseHref, self.depth, props)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.session.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+            pass
+        else:
+            raise RuntimeError("Propfind request failed: %s" % (request.getStatusCode(),))

Added: CalendarServer/trunk/contrib/performance/sqlusage/requests/query.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/requests/query.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/requests/query.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,110 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from caldavclientlibrary.protocol.url import URL
+from caldavclientlibrary.protocol.webdav.definitions import davxml, statuscodes
+from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
+from twext.web2.dav.util import joinURL
+from pycalendar.datetime import PyCalendarDateTime
+from caldavclientlibrary.protocol.caldav.query import QueryVEVENTTimeRange
+from caldavclientlibrary.protocol.http.data.string import ResponseDataString
+
+ICAL = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART:%s
+DURATION:PT1H
+SUMMARY:event 1
+UID:sync-collection-%d-ics
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+class QueryTest(HTTPTestBase):
+    """
+    A sync operation
+    """
+
+    def __init__(self, label, session, href, logFilePath, count):
+        super(QueryTest, self).__init__(label, session, href, logFilePath)
+        self.count = count
+    
+    def prepare(self):
+        """
+        Do some setup prior to the real request.
+        """
+        # Add resources to create required number of changes
+        self.start = PyCalendarDateTime.getNowUTC()
+        self.start.setHHMMSS(12, 0, 0)
+        self.end = self.start.duplicate()
+        self.end.offsetHours(1)
+        for i in range(self.count):
+            href = joinURL(self.baseHref, "tr-query-%d.ics" % (i+1,))
+            self.session.writeData(URL(path=href), ICAL % (self.start.getText(), i+1,), "text/calendar")
+
+    def doRequest(self):
+        """
+        Execute the actual HTTP request.
+        """
+        props = (
+            davxml.getetag,
+            davxml.getcontenttype,
+        )
+
+        # Create CalDAV query
+        request = QueryVEVENTTimeRange(self.session, self.baseHref, self.start.getText(), self.end.getText(), props)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.session.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+            pass
+        else:
+            raise RuntimeError("Query request failed: %s" % (request.getStatusCode(),))
+
+    def cleanup(self):
+        """
+        Do some cleanup after the real request.
+        """
+        # Remove created resources
+        for i in range(self.count):
+            href = joinURL(self.baseHref, "tr-query-%d.ics" % (i+1,))
+            self.session.deleteResource(URL(path=href))

Added: CalendarServer/trunk/contrib/performance/sqlusage/requests/sync.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/requests/sync.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/requests/sync.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,102 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from caldavclientlibrary.protocol.url import URL
+from caldavclientlibrary.protocol.webdav.definitions import davxml
+from contrib.performance.sqlusage.requests.httpTests import HTTPTestBase
+from twext.web2.dav.util import joinURL
+from pycalendar.datetime import PyCalendarDateTime
+
+ICAL = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:%d0101T100000
+DURATION:PT1H
+SUMMARY:event 1
+UID:sync-collection-%d-ics
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+class SyncTest(HTTPTestBase):
+    """
+    A sync operation
+    """
+
+    def __init__(self, label, session, href, logFilePath, full, count):
+        super(SyncTest, self).__init__(label, session, href, logFilePath)
+        self.full = full
+        self.count = count
+        self.synctoken = ""
+    
+    def prepare(self):
+        """
+        Do some setup prior to the real request.
+        """
+        if not self.full:
+            # Get current sync token
+            results, _ignore_bad = self.session.getProperties(URL(path=self.baseHref), (davxml.sync_token,))
+            self.synctoken = results[davxml.sync_token]
+            
+            # Add resources to create required number of changes
+            now = PyCalendarDateTime.getNowUTC()
+            for i in range(self.count):
+                href = joinURL(self.baseHref, "sync-collection-%d.ics" % (i+1,))
+                self.session.writeData(URL(path=href), ICAL % (now.getYear() + 1, i+1,), "text/calendar")
+
+    def doRequest(self):
+        """
+        Execute the actual HTTP request.
+        """
+        props = (
+            davxml.getetag,
+            davxml.getcontenttype,
+        )
+
+        # Run sync collection
+        self.session.syncCollection(URL(path=self.baseHref), self.synctoken, props)
+
+    def cleanup(self):
+        """
+        Do some cleanup after the real request.
+        """
+        if not self.full:
+            # Remove created resources
+            for i in range(self.count):
+                href = joinURL(self.baseHref, "sync-collection-%d.ics" % (i+1,))
+                self.session.deleteResource(URL(path=href))

Added: CalendarServer/trunk/contrib/performance/sqlusage/sqlusage.py
===================================================================
--- CalendarServer/trunk/contrib/performance/sqlusage/sqlusage.py	                        (rev 0)
+++ CalendarServer/trunk/contrib/performance/sqlusage/sqlusage.py	2012-08-06 16:47:35 UTC (rev 9528)
@@ -0,0 +1,214 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from StringIO import StringIO
+from caldavclientlibrary.client.clientsession import CalDAVSession
+from caldavclientlibrary.protocol.url import URL
+from caldavclientlibrary.protocol.webdav.definitions import davxml
+from calendarserver.tools import tables
+from contrib.performance.sqlusage.requests.multiget import MultigetTest
+from contrib.performance.sqlusage.requests.propfind import PropfindTest
+from contrib.performance.sqlusage.requests.query import QueryTest
+from contrib.performance.sqlusage.requests.sync import SyncTest
+from pycalendar.datetime import PyCalendarDateTime
+from twext.web2.dav.util import joinURL
+import getopt
+import sys
+
+"""
+This tool is designed to analyze how SQL is being used for various HTTP requests.
+It will execute a series of HTTP requests against a test server configuration and
+count the total number of SQL statements per request, the total number of rows
+returned per request and the total SQL execution time per request. Each series
+will be repeated against a varying calendar size so the variation in SQL use
+with calendar size can be plotted. 
+"""
+
+EVENT_COUNTS = (0, 1, 5, 10, 25, 50, 75, 100, 200, 300, 400, 500, 1000, 2000, 3000, 4000, 5000)
+
+ICAL = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:%d0101T100000
+DURATION:PT1H
+SUMMARY:event 1
+UID:%d-ics
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+class SQLUsage(object):
+    
+    def __init__(self, server, port, user, pswd, logFilePath):
+        self.server = server
+        self.port = port
+        self.user = user
+        self.pswd = pswd
+        self.logFilePath = logFilePath
+        self.requestLabels = []
+        self.results = {}
+        self.currentCount = 0
+        
+        self.userhref = "/calendars/users/%s/" % (self.user,)
+
+    def runLoop(self):
+        
+        # Make the session
+        session = CalDAVSession(self.server, self.port, user=self.user, pswd=self.pswd, root="/")
+
+        # Set of requests to execute
+        requests = [
+            MultigetTest("multiget-1", session, joinURL(self.userhref, "calendar/"), self.logFilePath, 1),
+            MultigetTest("multiget-50", session, joinURL(self.userhref, "calendar/"), self.logFilePath, 50),
+            PropfindTest("propfind-cal", session, joinURL(self.userhref, "calendar/"), self.logFilePath, 1),
+            SyncTest("sync-full", session, joinURL(self.userhref, "calendar/"), self.logFilePath, True, 0),
+            SyncTest("sync-1", session, joinURL(self.userhref, "calendar/"), self.logFilePath, False, 1),
+            QueryTest("query-1", session, joinURL(self.userhref, "calendar/"), self.logFilePath, 1),
+            QueryTest("query-10", session, joinURL(self.userhref, "calendar/"), self.logFilePath, 10),
+        ]
+        self.requestLabels = [request.label for request in requests]
+
+        # Warm-up server by doing calendar home and calendar propfinds
+        props = (davxml.resourcetype,)
+        session.getPropertiesOnHierarchy(URL(path=self.userhref), props)
+        session.getPropertiesOnHierarchy(URL(path=joinURL(self.userhref, "calendar/")), props)
+        
+        # Now loop over sets of events
+        for count in EVENT_COUNTS:
+            print "Testing count = %d" % (count,)
+            self.ensureEvents(session, count)
+            result = {}
+            for request in requests:
+                result[request.label] = request.execute()
+            self.results[count] = result
+    
+    def report(self):
+        
+        self._printReport("SQL Statement Count", "count", "%d")
+        self._printReport("SQL Rows Returned", "rows", "%d")
+        self._printReport("SQL Time", "timing", "%.3f")
+            
+    def _printReport(self, title, attr, colFormat):
+        table = tables.Table()
+        
+        print title
+        headers = ["Events"] + sorted([label for label in self.requestLabels])
+        table.addHeader(headers)
+        formats = [tables.Table.ColumnFormat("%d", tables.Table.ColumnFormat.RIGHT_JUSTIFY)] + \
+            [tables.Table.ColumnFormat(colFormat, tables.Table.ColumnFormat.RIGHT_JUSTIFY)] * len(self.requestLabels)
+        table.setDefaultColumnFormats(formats)
+        for k in sorted(self.results.keys()):
+            row = [k] + [getattr(self.results[k][item], attr) for item in sorted(self.results[k].keys())]
+            table.addRow(row)
+        os = StringIO()
+        table.printTable(os=os)
+        print os.getvalue()
+        print
+            
+    def ensureEvents(self, session, n):
+        """
+        Make sure the required number of events are present in the calendar.
+    
+        @param n: number of events
+        @type n: C{int}
+        """
+        now = PyCalendarDateTime.getNowUTC()
+        for i in range(n - self.currentCount):
+            index = self.currentCount + i + 1
+            href = joinURL(self.userhref, "calendar", "%d.ics" % (index,))
+            session.writeData(URL(path=href), ICAL % (now.getYear() + 1, index,), "text/calendar")
+            
+        self.currentCount = n
+
+def usage(error_msg=None):
+    if error_msg:
+        print error_msg
+
+    print """Usage: sqlusage.py [options] [FILE]
+Options:
+    -h             Print this help and exit
+    --server       Server hostname
+    --port         Server port
+    --user         User name
+    --pswd         Password
+
+Arguments:
+    FILE           File name for sqlstats.log to analyze.
+
+Description:
+This utility will analyze the output of s pg_stat_statement table.
+"""
+
+    if error_msg:
+        raise ValueError(error_msg)
+    else:
+        sys.exit(0)
+
+if __name__ == '__main__':
+    
+    server = "localhost"
+    port = 8008
+    user = "user01"
+    pswd = "user01"
+    file = "sqlstats.logs"
+
+    options, args = getopt.getopt(sys.argv[1:], "h", ["server", "port", "user", "pswd",])
+
+    for option, value in options:
+        if option == "-h":
+            usage()
+        elif option == "--server":
+            server = value
+        elif option == "--port":
+            port = int(value)
+        elif option == "--user":
+            user = value
+        elif option == "--pswd":
+            pswd = value
+        else:
+            usage("Unrecognized option: %s" % (option,))
+
+    # Process arguments
+    if len(args) == 1:
+        file = args[0]
+    elif len(args) != 0:
+        usage("Must zero or one file arguments")
+
+    sql = SQLUsage(server, port, user, pswd, file)
+    sql.runLoop()
+    sql.report()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120806/e3dd3ea8/attachment-0001.html>


More information about the calendarserver-changes mailing list