[CalendarServer-changes] [8401] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Sat Dec 3 00:43:59 PST 2011


Revision: 8401
          http://trac.macosforge.org/projects/calendarserver/changeset/8401
Author:   glyph at apple.com
Date:     2011-12-03 00:43:59 -0800 (Sat, 03 Dec 2011)
Log Message:
-----------
Land parallel-upgrade branch.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tap/caldav.py
    CalendarServer/trunk/calendarserver/tap/util.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/txdav/common/datastore/test/util.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/migrate.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py

Added Paths:
-----------
    CalendarServer/trunk/bin/calendarserver_upgrade
    CalendarServer/trunk/calendarserver/tap/cfgchild.py
    CalendarServer/trunk/calendarserver/tools/upgrade.py
    CalendarServer/trunk/twext/internet/spawnsvc.py

Property Changed:
----------------
    CalendarServer/trunk/
    CalendarServer/trunk/support/build.sh
    CalendarServer/trunk/txdav/caldav/datastore/index_file.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
    CalendarServer/trunk/txdav/carddav/datastore/index_file.py
    CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py


Property changes on: CalendarServer/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593

Copied: CalendarServer/trunk/bin/calendarserver_upgrade (from rev 8400, CalendarServer/branches/users/glyph/parallel-upgrade/bin/calendarserver_upgrade)
===================================================================
--- CalendarServer/trunk/bin/calendarserver_upgrade	                        (rev 0)
+++ CalendarServer/trunk/bin/calendarserver_upgrade	2011-12-03 08:43:59 UTC (rev 8401)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2007 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.
+##
+
+import sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+    if "PYTHONPATH" in globals():
+        sys.path.insert(0, PYTHONPATH)
+    else:
+        try:
+            import _calendarserver_preamble
+        except ImportError:
+            sys.exc_clear()
+
+    from calendarserver.tools.upgrade import main
+    main()

Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py	2011-12-03 08:40:33 UTC (rev 8400)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -34,15 +34,18 @@
 
 from zope.interface import implements
 
+from twisted.plugin import IPlugin
+
 from twisted.python.log import FileLogObserver, ILogObserver
 from twisted.python.logfile import LogFile
 from twisted.python.usage import Options, UsageError
-from twisted.plugin import IPlugin
+
 from twisted.internet.defer import gatherResults, Deferred
-from twisted.internet import reactor as _reactor
+
 from twisted.internet.process import ProcessExitedAlready
 from twisted.internet.protocol import Protocol, Factory
 from twisted.internet.protocol import ProcessProtocol
+
 from twisted.application.internet import TCPServer, UNIXServer
 from twisted.application.service import MultiService, IServiceMaker
 from twisted.application.service import Service
@@ -57,9 +60,10 @@
 from twext.web2.channel.http import LimitingHTTPFactory, SSLRedirectRequest
 from twext.web2.metafd import ConnectionLimiter, ReportingHTTPService
 
+from txdav.common.datastore.upgrade.sql.upgrade import (
+    UpgradeDatabaseSchemaService, UpgradeDatabaseDataService,
+)
 from txdav.common.datastore.upgrade.migrate import UpgradeToDatabaseService
-from txdav.common.datastore.upgrade.sql.upgrade import UpgradeDatabaseSchemaService,\
-    UpgradeDatabaseDataService
 
 from twistedcaldav.config import ConfigurationError
 from twistedcaldav.config import config
@@ -74,7 +78,6 @@
 from twext.enterprise.ienterprise import POSTGRES_DIALECT
 from twext.enterprise.ienterprise import ORACLE_DIALECT
 from twext.enterprise.adbapi2 import ConnectionPool
-from twext.enterprise.adbapi2 import ConnectionPoolConnection
 
 try:
     from twistedcaldav.authkerb import NegotiateCredentialFactory
@@ -82,14 +85,17 @@
 except ImportError:
     NegotiateCredentialFactory = None
 
+from calendarserver.tap.util import ConnectionDispenser
+
 from calendarserver.accesslog import AMPCommonAccessLoggingObserver
 from calendarserver.accesslog import AMPLoggingFactory
 from calendarserver.accesslog import RotatingFileAccessLoggingObserver
 from calendarserver.tap.util import getRootResource, computeProcessCount
-from calendarserver.tap.util import ConnectionWithPeer
+
 from calendarserver.tap.util import storeFromConfig
 from calendarserver.tap.util import pgConnectorFromConfig
 from calendarserver.tap.util import oracleConnectorFromConfig
+from calendarserver.tap.cfgchild import ConfiguredChildSpawner
 from calendarserver.tools.util import checkDirectory
 
 try:
@@ -208,6 +214,7 @@
 
         self.overrides = {}
 
+
     @staticmethod
     def coerceOption(configDict, key, value):
         """
@@ -233,6 +240,7 @@
 
         return value
 
+
     @classmethod
     def setOverride(cls, configDict, path, value, overrideDict):
         """
@@ -893,6 +901,47 @@
 
         @rtype: L{IService}
         """
+        def createSubServiceFactory(dialect=POSTGRES_DIALECT,
+                                    paramstyle='pyformat'):
+            def subServiceFactory(connectionFactory):
+                ms = MultiService()
+                cp = ConnectionPool(connectionFactory, dialect=dialect,
+                                    paramstyle=paramstyle,
+                                    maxConnections=config.MaxDBConnectionsPerPool)
+                cp.setServiceParent(ms)
+                store = storeFromConfig(config, cp.connection)
+                mainService = createMainService(cp, store)
+                if config.ParallelUpgrades:
+                    parallel = config.MultiProcess.ProcessCount
+                else:
+                    parallel = 0
+                upgradeSvc = UpgradeFileSystemFormatService(
+                    config,
+                    UpgradeDatabaseSchemaService.wrapService(
+                        UpgradeDatabaseDataService.wrapService(
+                            UpgradeToDatabaseService.wrapService(
+                                CachingFilePath(config.DocumentRoot),
+                                PostDBImportService(config, store, mainService),
+                                store, uid=overrideUID, gid=overrideGID,
+                                spawner=ConfiguredChildSpawner(
+                                    self, ConnectionDispenser(cp), config
+                                ),
+                                parallel=parallel
+                            ),
+                            store, uid=overrideUID, gid=overrideGID,
+                        ),
+                        store, uid=overrideUID, gid=overrideGID,
+                    )
+                )
+                upgradeSvc.setServiceParent(ms)
+                return ms
+            return subServiceFactory
+
+        # FIXME: this is replicating the logic of getDBPool(), except for the
+        # part where the pgServiceFromConfig service is actually started here,
+        # and discarded in that function.  This should be refactored to simply
+        # use getDBPool.
+
         if config.UseDatabase:
 
             if os.getuid() == 0: # Only override if root
@@ -907,22 +956,19 @@
                 # to it.
                 pgserv = pgServiceFromConfig(
                     config,
-                    self.subServiceFactoryFactory(createMainService,
-                        uid=overrideUID, gid=overrideGID),
+                    createSubServiceFactory(),
                     uid=overrideUID, gid=overrideGID
                 )
                 return pgserv
             elif config.DBType == 'postgres':
                 # Connect to a postgres database that is already running.
-                return self.subServiceFactoryFactory(createMainService,
-                    uid=overrideUID, gid=overrideGID)(
-                            pgConnectorFromConfig(config))
+                return createSubServiceFactory()(pgConnectorFromConfig(config))
             elif config.DBType == 'oracle':
                 # Connect to an Oracle database that is already running.
-                return self.subServiceFactoryFactory(createMainService,
-                    uid=overrideUID, gid=overrideGID,
-                    dialect=ORACLE_DIALECT, paramstyle='numeric')(
-                            oracleConnectorFromConfig(config))
+                return createSubServiceFactory(dialect=ORACLE_DIALECT,
+                                               paramstyle='numeric')(
+                    oracleConnectorFromConfig(config)
+                )
             else:
                 raise UsageError("Unknown database type %r" (config.DBType,))
         else:
@@ -930,35 +976,6 @@
             return createMainService(None, store)
 
 
-    def subServiceFactoryFactory(self, createMainService, uid=None, gid=None,
-                                 dialect=POSTGRES_DIALECT,
-                                 paramstyle='pyformat'):
-        def subServiceFactory(connectionFactory):
-            ms = MultiService()
-            cp = ConnectionPool(connectionFactory, dialect=dialect,
-                                paramstyle=paramstyle,
-                                maxConnections=config.MaxDBConnectionsPerPool)
-            cp.setServiceParent(ms)
-            store = storeFromConfig(config, cp.connection)
-            mainService = createMainService(cp, store)
-            upgradeSvc = UpgradeFileSystemFormatService(config,
-                UpgradeDatabaseSchemaService.wrapService(
-                    UpgradeDatabaseDataService.wrapService(
-                        UpgradeToDatabaseService.wrapService(
-                            CachingFilePath(config.DocumentRoot),
-                            PostDBImportService(config, store, mainService),
-                            store, uid=uid, gid=gid
-                        ),
-                        store, uid=uid, gid=gid
-                    ),
-                    store, uid=uid, gid=gid
-                )
-            )
-            upgradeSvc.setServiceParent(ms)
-            return ms
-        return subServiceFactory
-
-
     def makeService_Combined(self, options):
         """
         Create a master service to coordinate a multi-process configuration,
@@ -1032,6 +1049,7 @@
         # Calculate the number of processes to spawn
         #
         if config.MultiProcess.ProcessCount == 0:
+            # TODO: this should probably be happening in a configuration hook.
             processCount = computeProcessCount(
                 config.MultiProcess.MinProcessCount,
                 config.MultiProcess.PerCPU,
@@ -1150,28 +1168,6 @@
 
 
 
-class ConnectionDispenser(object):
-
-    def __init__(self, connectionPool):
-        self.pool = connectionPool
-
-
-    def dispense(self):
-        """
-        Dispense a file descriptor, already connected to a server, for a
-        client.
-        """
-        # FIXME: these sockets need to be re-dispensed when the process is
-        # respawned, and they currently won't be.
-        c, s = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        protocol = ConnectionPoolConnection(self.pool)
-        transport = ConnectionWithPeer(s, protocol)
-        protocol.makeConnection(transport)
-        transport.startReading()
-        return c
-
-
-
 class TwistdSlaveProcess(object):
     """
     A L{TwistdSlaveProcess} is information about how to start a slave process
@@ -1315,6 +1311,7 @@
         return args
 
 
+
 class ControlPortTCPServer(TCPServer):
     """ This TCPServer retrieves the port number that was actually assigned
         when the service was started, and stores that into config.ControlPort
@@ -1354,8 +1351,10 @@
     minRestartDelay = 1
     maxRestartDelay = 3600
 
-    def __init__(self, reactor=_reactor):
+    def __init__(self, reactor=None):
         super(DelayedStartupProcessMonitor, self).__init__()
+        if reactor is None:
+            from twisted.internet import reactor
         self._reactor = reactor
         self.processes = {}
         self.protocols = {}

Copied: CalendarServer/trunk/calendarserver/tap/cfgchild.py (from rev 8400, CalendarServer/branches/users/glyph/parallel-upgrade/calendarserver/tap/cfgchild.py)
===================================================================
--- CalendarServer/trunk/calendarserver/tap/cfgchild.py	                        (rev 0)
+++ CalendarServer/trunk/calendarserver/tap/cfgchild.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -0,0 +1,188 @@
+##
+# Copyright (c) 2005-2011 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.
+##
+
+"""
+Tools for spawning general-purpose child processes that have a store derived
+from an existing configuration.
+"""
+
+__all__ = [
+    # Only the spawner service is really interesting; the other parts are
+    # internal implementation details which shouldn't be needed outside this
+    # file.
+    'ConfiguredChildSpawner',
+]
+
+from calendarserver.tools.util import setupMemcached
+from twisted.python.reflect import namedAny, qual
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.protocols.amp import AMP, Command, String, Integer#, ListOf
+from txdav.common.datastore.upgrade.migrate import (
+    StoreSpawnerService, swapAMP
+)
+
+from calendarserver.tap.util import getDBPool, storeFromConfig
+
+class ConfigureChild(Command):
+    """
+    Configure a child process, most especially with all the information that it
+    needs in order to construct a data store.
+    """
+
+    arguments = [
+        # The name of the class to delegate to once configuration is complete.
+        ("delegateTo", String()),
+        ("pidFile", String()),
+        ("logID", String()),
+        ("configFile", String()),
+
+        # computed value determined only in master, so needs to be propagated
+        # to be correct.
+        ("processCount", Integer()),
+
+        ## only needed for request processing, and we're not using this
+        ## facility for that work (yet)
+        # ("inheritFDs", ListOf(Integer())),
+        # ("inheritSSLFDs", ListOf(Integer())),
+        # ("metaFD", String(optional=True)),
+
+        ## shared connection pool!
+        ("connectionPoolFD", Integer(optional=True)),
+    ]
+
+
+
+class ChildConfigurator(AMP):
+    """
+    Protocol which can configure a child process.
+    """
+
+    def __init__(self, config=None):
+        """
+        Optionally accept a configuration for testing, but normally created in
+        the subprocess configuration-free.
+        """
+        super(ChildConfigurator, self).__init__()
+        if config is None:
+            from twistedcaldav.config import config
+        self.config = config
+
+
+    @ConfigureChild.responder
+    def conf(self, delegateTo, pidFile, logID, configFile, processCount,
+             connectionPoolFD=None):
+        """
+        Load the current config file into this child process, create a store
+        based on it, and delegate to the upgrade logic.
+        """
+        # Load the configuration file.
+        self.config.load(configFile)
+
+        # Adjust the child's configuration to add all the relevant options for
+        # the store that won't be mentioned in the config file.
+        changedConfig = dict(
+            EnableCalDAV  = True,
+            EnableCardDAV = True,
+            LogID         = logID,
+            PIDFile       = pidFile,
+            MultiProcess  = dict(
+                ProcessCount = processCount
+            )
+        )
+        setupMemcached(self.config)
+        if connectionPoolFD is not None:
+            changedConfig.update(DBAMPFD=connectionPoolFD)
+        self.config.updateDefaults(changedConfig)
+
+        # Construct and start database pool and store.
+        pool, txnf = getDBPool(self.config)
+        if pool is not None:
+            from twisted.internet import reactor
+            pool.startService()
+            reactor.addSystemEventTrigger(
+                "before", "shutdown", pool.stopService
+            )
+        dbstore = storeFromConfig(self.config, txnf)
+
+        # Finally, construct the class we're supposed to delegate to.
+        delegateClass = namedAny(delegateTo)
+        swapAMP(self, delegateClass(dbstore))
+        return {}
+
+
+
+class ConfiguredChildSpawner(StoreSpawnerService):
+    """
+    L{StoreSpawnerService} that will load a full configuration into each child.
+    """
+
+    def __init__(self, maker, dispenser, config):
+        """
+        Create a L{ConfiguredChildSpawner}.
+
+        @param maker: a L{CalDAVServiceMaker} instance that supplies the
+            configuration.
+
+        @param dispenser: a L{calendarserver.tap.ConnectionDispenser} or C{None}.
+
+        @param config: the L{twistedcaldav.config.Config} to use to configure
+            the subprocess.
+        """
+        super(ConfiguredChildSpawner, self).__init__()
+        self.nextID = 0
+        self.maker = maker
+        self.dispenser = dispenser
+        self.config = config
+
+
+    @inlineCallbacks
+    def spawnWithStore(self, here, there):
+        """
+        Spawn the child with a store based on a configuration.
+        """
+        thisID = self.nextID
+        self.nextID += 1
+        if self.dispenser is not None:
+            # NOTE: important and super subtle, 'poolskt' must not be GC'd
+            # until the call to spawn (and hence spawnProcess) below; otherwise
+            # that end of the socket will be closed and there will be nothing
+            # to inherit.
+            poolskt  = self.dispenser.dispense()
+            poolfd   = poolskt.fileno()
+            childFDs = {
+                0: "w", 1: "r", 2: "r", # behave like normal, but
+                poolfd: poolfd          # bonus FD
+            }
+            extra    = dict(connectionPoolFD=poolfd)
+        else:
+            childFDs = None
+            extra    = {}
+        controller = yield self.spawn(
+            AMP(), ChildConfigurator, childFDs=childFDs
+        )
+        yield controller.callRemote(
+            ConfigureChild,
+            delegateTo=qual(there),
+            pidFile="%s-migrator-%s" % (self.maker.tapname, thisID),
+            logID="migrator-%s" % (thisID,),
+            configFile=self.config.getProvider().getConfigFileName(),
+            processCount=self.config.MultiProcess.ProcessCount,
+            **extra
+        )
+        returnValue(swapAMP(controller, here))
+
+
+

Modified: CalendarServer/trunk/calendarserver/tap/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/util.py	2011-12-03 08:40:33 UTC (rev 8400)
+++ CalendarServer/trunk/calendarserver/tap/util.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -28,7 +28,7 @@
 import errno
 import os
 from time import sleep
-from socket import fromfd, AF_UNIX, SOCK_STREAM
+from socket import fromfd, AF_UNIX, SOCK_STREAM, socketpair
 
 from twext.python.filepath import CachingFilePath as FilePath
 from twext.python.log import Logger
@@ -67,7 +67,7 @@
 from twistedcaldav.util import getMemorySize, getNCPU
 from twext.enterprise.ienterprise import POSTGRES_DIALECT
 from twext.enterprise.ienterprise import ORACLE_DIALECT
-from twext.enterprise.adbapi2 import ConnectionPool
+from twext.enterprise.adbapi2 import ConnectionPool, ConnectionPoolConnection
 
 try:
     from twistedcaldav.authkerb import NegotiateCredentialFactory
@@ -163,7 +163,8 @@
 
 def transactionFactoryFromFD(dbampfd, dialect, paramstyle):
     """
-    Create a transaction factory from an inherited file descriptor.
+    Create a transaction factory from an inherited file descriptor, such as one
+    created by L{ConnectionDispenser}.
     """
     skt = fromfd(dbampfd, AF_UNIX, SOCK_STREAM)
     os.close(dbampfd)
@@ -175,6 +176,35 @@
 
 
 
+class ConnectionDispenser(object):
+    """
+    A L{ConnectionDispenser} can dispense already-connected file descriptors,
+    for use with subprocess spawning.
+    """
+    # Very long term FIXME: this mechanism should ideally be eliminated, by
+    # making all subprocesses have a single stdio AMP connection that
+    # multiplexes between multiple protocols.
+
+    def __init__(self, connectionPool):
+        self.pool = connectionPool
+
+
+    def dispense(self):
+        """
+        Dispense a socket object, already connected to a server, for a client
+        in a subprocess.
+        """
+        # FIXME: these sockets need to be re-dispensed when the process is
+        # respawned, and they currently won't be.
+        c, s = socketpair(AF_UNIX, SOCK_STREAM)
+        protocol = ConnectionPoolConnection(self.pool)
+        transport = ConnectionWithPeer(s, protocol)
+        protocol.makeConnection(transport)
+        transport.startReading()
+        return c
+
+
+
 def storeFromConfig(config, txnFactory):
     """
     Produce an L{IDataStore} from the given configuration, transaction factory,

Copied: CalendarServer/trunk/calendarserver/tools/upgrade.py (from rev 8400, CalendarServer/branches/users/glyph/parallel-upgrade/calendarserver/tools/upgrade.py)
===================================================================
--- CalendarServer/trunk/calendarserver/tools/upgrade.py	                        (rev 0)
+++ CalendarServer/trunk/calendarserver/tools/upgrade.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+# -*- test-case-name: calendarserver.tools.test.test_upgrade -*-
+##
+# Copyright (c) 2006-2011 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.
+##
+
+"""
+This tool allows any necessary upgrade to complete, then exits.
+"""
+
+import os
+import sys
+
+from twisted.python.text import wordWrap
+from twisted.python import log
+from twisted.python.usage import Options, UsageError
+
+from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
+from calendarserver.tools.cmdline import utilityMain
+from twisted.application.service import Service
+
+
+from twext.python.log import setLogLevelForNamespace
+
+def usage(e=None):
+    if e:
+        print e
+        print ""
+    try:
+        UpgradeOptions().opt_help()
+    except SystemExit:
+        pass
+    if e:
+        sys.exit(64)
+    else:
+        sys.exit(0)
+
+
+description = '\n'.join(
+    wordWrap(
+        """
+        Usage: calendarserver_upgrade [options] [input specifiers]\n
+        """ + __doc__,
+        int(os.environ.get('COLUMNS', '80'))
+    )
+)
+
+class UpgradeOptions(Options):
+    """
+    Command-line options for 'calendarserver_upgrade'
+
+    @ivar upgradeers: a list of L{DirectoryUpgradeer} objects which can identify the
+        calendars to upgrade, given a directory service.  This list is built by
+        parsing --record and --collection options.
+    """
+
+    synopsis = description
+
+    optParameters = [['config', 'f', DEFAULT_CONFIG_FILE,
+                      "Specify caldavd.plist configuration path."]]
+
+    def __init__(self):
+        super(UpgradeOptions, self).__init__()
+        self.upgradeers = []
+        self.outputName = '-'
+
+
+    def opt_output(self, filename):
+        """
+        Specify output file path (default: '-', meaning stdout).
+        """
+        self.outputName = filename
+
+    opt_o = opt_output
+
+
+    def openOutput(self):
+        """
+        Open the appropriate output file based on the '--output' option.
+        """
+        if self.outputName == '-':
+            return sys.stdout
+        else:
+            return open(self.outputName, 'wb')
+
+
+
+class UpgraderService(Service, object):
+    """
+    Service which runs, exports the appropriate records, then stops the reactor.
+    """
+
+    def __init__(self, store, options, output, reactor, config):
+        super(UpgraderService, self).__init__()
+        self.store   = store
+        self.options = options
+        self.output  = output
+        self.reactor = reactor
+        self.config = config
+        self._directory = None
+
+
+    def startService(self):
+        """
+        Immediately stop.  The upgrade will have been run before this.
+        """
+        self.output.write("Upgrade complete, shutting down.\n")
+        from twisted.internet import reactor
+        from twisted.internet.error import ReactorNotRunning
+        try:
+            reactor.stop()
+        except ReactorNotRunning:
+            # I don't care.
+            pass
+
+
+    def stopService(self):
+        """
+        Stop the service.  Nothing to do; everything should be finished by this
+        time.
+        """
+
+from txdav.common.datastore import upgrade
+from txdav.common.datastore.upgrade import migrate
+
+
+def main(argv=sys.argv, stderr=sys.stderr, reactor=None):
+    """
+    Do the export.
+    """
+    if reactor is None:
+        from twisted.internet import reactor
+
+    options = UpgradeOptions()
+    try:
+        options.parseOptions(argv[1:])
+    except UsageError, e:
+        usage(e)
+
+    try:
+        output = options.openOutput()
+    except IOError, e:
+        stderr.write("Unable to open output file for writing: %s\n" %
+                     (e))
+        sys.exit(1)
+
+    def makeService(store):
+        from twistedcaldav.config import config
+        return UpgraderService(store, options, output, reactor, config)
+
+    def onlyUpgradeEvents(event):
+        if event.get("namespace", "").startswith(upgrade.__name__):
+            output.write(log.textFromEventDict(event)+"\n")
+
+    setLogLevelForNamespace(upgrade.__name__, "info")
+    setLogLevelForNamespace(migrate.__name__, "info")
+    log.addObserver(onlyUpgradeEvents)
+    utilityMain(options["config"], makeService, reactor)


Property changes on: CalendarServer/trunk/support/build.sh
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/support/build.sh:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/support/build.sh:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pods/support/build.sh:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/support/build.sh:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/support/build.sh:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/support/build.sh:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/new-export/support/build.sh:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/applepush/support/build.sh:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/support/build.sh:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593
   + /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/support/build.sh:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/support/build.sh:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pods/support/build.sh:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/support/build.sh:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/support/build.sh:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/support/build.sh:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/new-export/support/build.sh:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/support/build.sh:8376-8400
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/applepush/support/build.sh:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/support/build.sh:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593

Copied: CalendarServer/trunk/twext/internet/spawnsvc.py (from rev 8400, CalendarServer/branches/users/glyph/parallel-upgrade/twext/internet/spawnsvc.py)
===================================================================
--- CalendarServer/trunk/twext/internet/spawnsvc.py	                        (rev 0)
+++ CalendarServer/trunk/twext/internet/spawnsvc.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -0,0 +1,261 @@
+##
+# Copyright (c) 2011 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.
+##
+
+"""
+Utility service that can spawn subprocesses.
+"""
+
+import os
+import sys
+
+from twisted.python import log
+
+from twisted.python.reflect import namedAny
+from twisted.internet.stdio import StandardIO
+from twisted.internet.error import ReactorNotRunning
+
+if __name__ == '__main__':
+
+    sys.stdout = sys.stderr
+    there = sys.argv[1]
+    protocolClass = namedAny(there)
+    proto = protocolClass()
+    origLost = proto.connectionLost
+    def goodbye(reason):
+        """
+        Stop the process if stdin is closed.
+        """
+        try:
+            reactor.stop()
+        except ReactorNotRunning:
+            pass
+        return origLost(reason)
+    proto.connectionLost = goodbye
+    StandardIO(proto)
+    from twisted.internet import reactor
+    reactor.run()
+    os._exit(0)
+
+
+import sys
+
+from zope.interface import implements
+
+from twisted.internet.interfaces import ITransport, IPushProducer, IConsumer
+
+from twisted.application.service import Service
+from twisted.python.reflect import qual
+from twisted.internet.protocol import ProcessProtocol
+from twisted.internet.defer import Deferred, succeed
+
+
+class BridgeTransport(object):
+    """
+    ITransport implementation for the protocol in the parent process running a
+    L{SpawnerService}.
+    """
+
+    implements(ITransport, IPushProducer, IConsumer)
+
+    def __init__(self, processTransport):
+        """
+        Create this bridge transport connected to an L{IProcessTransport}.
+        """
+        self.transport = processTransport
+
+
+    def __getattr__(self, name):
+        """
+        Delegate all attribute accesses to the process traansport.
+        """
+        return getattr(self.transport, name)
+
+
+    def getPeer(self):
+        """
+        Get a fake peer address indicating the subprocess's pid.
+        """
+        return "Peer:PID:" + str(self.transport.pid)
+
+
+    def getHost(self):
+        """
+        Get a fake host address indicating the subprocess's pid.
+        """
+        return "Host:PID:" + str(self.transport.pid)
+
+
+
+class BridgeProtocol(ProcessProtocol, object):
+    """
+    Process protocol implementation that delivers data to the C{hereProto}
+    associated with an invocation of L{SpawnerService.spawn}.
+
+    @ivar service: a L{SpawnerService} that created this L{BridgeProtocol}
+
+    @ivar protocol: a reference to the L{IProtocol}.
+
+    @ivar killTimeout: number of seconds after sending SIGINT that this process
+        will send SIGKILL.
+    """
+
+    def __init__(self, service, protocol, killTimeout=15.0):
+        self.service = service
+        self.protocol = protocol
+        self.killTimeout = killTimeout
+        self.service.addBridge(self)
+
+
+    def connectionMade(self):
+        """
+        The subprocess was started.
+        """
+        self.protocol.makeConnection(BridgeTransport(self.transport))
+
+
+    def outReceived(self, data):
+        """
+        Some data was received to standard output; relay it to the protocol.
+        """
+        self.protocol.dataReceived(data)
+
+
+    def errReceived(self, data):
+        """
+        Some standard error was received from the subprocess.
+        """
+        log.msg("Error output from process: " + data,
+                isError=True)
+
+
+    _killTimeout = None
+    def eventuallyStop(self):
+        """
+        Eventually stop this subprocess.  Send it a SIGTERM, and if it hasn't
+        stopped by C{self.killTimeout} seconds, send it a SIGKILL.
+        """
+        self.transport.signalProcess('TERM')
+        def reallyStop():
+            self.transport.signalProcess("KILL")
+            self._killTimeout = None
+        self._killTimeout = (
+            self.service.reactor.callLater(self.killTimeout, reallyStop)
+        )
+
+
+    def processEnded(self, reason):
+        """
+        The process has ended; notify the L{SpawnerService} that this bridge
+        has stopped.
+        """
+        if self._killTimeout is not None:
+            self._killTimeout.cancel()
+        self.protocol.connectionLost(reason)
+        self.service.removeBridge(self)
+
+
+
+class SpawnerService(Service, object):
+    """
+    Process to spawn services and then shut them down.
+
+    @ivar reactor: an L{IReactorProcess}/L{IReactorTime}
+
+    @ivar pendingSpawns: a C{list} of 2-C{tuple}s of hereProto, thereProto.
+
+    @ivar bridges: a C{list} of L{BridgeProtocol} instances.
+    """
+
+    def __init__(self, reactor=None):
+        if reactor is None:
+            from twisted.internet import reactor
+        self.reactor = reactor
+        self.pendingSpawns = []
+        self.bridges = []
+        self._stopAllDeferred = None
+
+
+    def spawn(self, hereProto, thereProto, childFDs=None):
+        """
+        Spawn a subprocess with a connected pair of protocol objects, one in
+        the current process, one in the subprocess.
+
+        @param hereProto: a L{Protocol} instance to listen in this process.
+
+        @param thereProto: a top-level class or function that will be imported
+            and called in the spawned subprocess.
+
+        @param childFDs: File descriptors to share with the subprocess; same
+            format as L{IReactorProcess.spawnProcess}.
+
+        @return: a L{Deferred} that fires when C{hereProto} is ready.
+        """
+        if not self.running:
+            self.pendingSpawns.append((hereProto, thereProto))
+            return
+        name = qual(thereProto)
+        argv = [sys.executable, '-u', '-m', __name__, name]
+        self.reactor.spawnProcess(
+            BridgeProtocol(self, hereProto), sys.executable,
+            argv, os.environ, childFDs=childFDs
+        )
+        return succeed(hereProto)
+
+
+    def startService(self):
+        """
+        Start the service; spawn any processes previously started with spawn().
+        """
+        super(SpawnerService, self).startService()
+        for spawn in self.pendingSpawns:
+            self.spawn(*spawn)
+
+
+    def addBridge(self, bridge):
+        """
+        Add a L{BridgeProtocol} to the list to be tracked.
+        """
+        self.bridges.append(bridge)
+
+
+    def removeBridge(self, bridge):
+        """
+        The process controlled by a L{BridgeProtocol} has terminated; remove it
+        from the active list, and fire any outstanding Deferred.
+
+        @param bridge: the protocol which has ended.
+        """
+        self.bridges.remove(bridge)
+        if self._stopAllDeferred is not None:
+            if len(self.bridges) == 0:
+                self._stopAllDeferred.callback(None)
+                self._stopAllDeferred = False
+
+
+    def stopService(self):
+        """
+        Stop the service.
+        """
+        super(SpawnerService, self).stopService()
+        if self.bridges:
+            self._stopAllDeferred = Deferred()
+            for bridge in self.bridges:
+                bridge.eventuallyStop()
+            return self._stopAllDeferred
+        return succeed(None)
+
+
+

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2011-12-03 08:40:33 UTC (rev 8400)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -530,7 +530,12 @@
                                                    # split existing calendars into multiples based on component type.
                                                    # If on, it will also cause new accounts to provision with separate
                                                    # calendars for events and tasks.
-    
+
+    "ParallelUpgrades" : False,    # Perform upgrades - currently only the
+                                   # database -> filesystem migration - but in
+                                   # the future, hopefully all relevant
+                                   # upgrades - in parallel in subprocesses.
+
     "EnableDefaultAlarms" : True, # Support for default alarms generated by the server
 
     # CardDAV Features


Property changes on: CalendarServer/trunk/txdav/caldav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/caldav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/caldav/datastore/index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/caldav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/caldav/datastore/index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/caldav/datastore/index_file.py:8376-8400
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394


Property changes on: CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/test_index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/caldav/datastore/test/test_index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/test_index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/caldav/datastore/test/test_index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/caldav/datastore/test/test_index_file.py:8376-8400
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394


Property changes on: CalendarServer/trunk/txdav/carddav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/carddav/datastore/index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/carddav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/carddav/datastore/index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/carddav/datastore/index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/carddav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/carddav/datastore/index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/carddav/datastore/index_file.py:8376-8400
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394


Property changes on: CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/carddav/datastore/test/test_index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/carddav/datastore/test/test_index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
   + /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/carddav/datastore/test/test_index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/carddav/datastore/test/test_index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/carddav/datastore/test/test_index_file.py:8376-8400
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394

Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py	2011-12-03 08:40:33 UTC (rev 8400)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -43,6 +43,7 @@
 from txdav.base.propertystore.base import PropertyName
 from txdav.common.icommondatastore import NoSuchHomeChildError
 from twext.enterprise.adbapi2 import ConnectionPool
+from twisted.trial.unittest import TestCase
 from twisted.internet.defer import returnValue
 from twistedcaldav.notify import Notifier, NodeCreationException
 from twext.enterprise.ienterprise import AlreadyFinishedError
@@ -83,6 +84,48 @@
 
     SHARED_DB_PATH = "_test_sql_db"
 
+
+    @classmethod
+    def createService(cls, serviceFactory):
+        """
+        Create a L{PostgresService} to use for building a store.
+        """
+        dbRoot = CachingFilePath(cls.SHARED_DB_PATH)
+        return PostgresService(
+            dbRoot, serviceFactory, current_sql_schema, resetSchema=True,
+            databaseName="caldav",
+            options = [
+                "-c log_lock_waits=TRUE",
+                "-c log_statement=all",
+                "-c log_line_prefix='%p.%x '",
+            ],
+            testMode=True
+        )
+
+
+    @classmethod
+    def childStore(cls):
+        """
+        Create a store suitable for use in a child process, that is hooked up
+        to the store that a parent test process is managing.
+        """
+        disableMemcacheForTest(TestCase())
+        staticQuota = 3000
+        attachmentRoot = (CachingFilePath(cls.SHARED_DB_PATH)
+                          .child("attachments"))
+        stubsvc = cls.createService(lambda cf: Service())
+
+        cp = ConnectionPool(stubsvc.produceConnection, maxConnections=1)
+        # Attach the service to the running reactor.
+        cp.startService()
+        reactor.addSystemEventTrigger("before", "shutdown", cp.stopService)
+        cds = CommonDataStore(
+            cp.connection, StubNotifierFactory(),
+            attachmentRoot, quota=staticQuota
+        )
+        return cds
+
+
     def buildStore(self, testCase, notifierFactory):
         """
         Do the necessary work to build a store for a particular test case.
@@ -99,16 +142,7 @@
                     testCase, notifierFactory, attachmentRoot
                 ).chainDeferred(ready)
                 return Service()
-            self.sharedService = PostgresService(
-                dbRoot, getReady, current_sql_schema, resetSchema=True,
-                databaseName="caldav",
-                options = [
-                    "-c log_lock_waits=TRUE",
-                    "-c log_statement=all",
-                    "-c log_line_prefix='%p.%x '",
-                ],
-                testMode=True
-            )
+            self.sharedService = self.createService(getReady)
             self.sharedService.startService()
             def startStopping():
                 log.msg("Starting stopping.")
@@ -145,7 +179,8 @@
         except OSError:
             pass
         currentTestID = testCase.id()
-        cp = ConnectionPool(self.sharedService.produceConnection)
+        cp = ConnectionPool(self.sharedService.produceConnection,
+                            maxConnections=5)
         quota = deriveQuota(testCase)
         store = CommonDataStore(
             cp.connection, notifierFactory, attachmentRoot, quota=quota

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/migrate.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/migrate.py	2011-12-03 08:40:33 UTC (rev 8400)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/migrate.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -24,25 +24,175 @@
 import xattr
 
 from twext.python.log import LoggingMixIn
+
 from twisted.application.service import Service
 from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import maybeDeferred, DeferredList
 from twisted.python.runtime import platform
 
+from twext.python.filepath import CachingFilePath
+from twext.internet.spawnsvc import SpawnerService
+
+from twisted.protocols.amp import AMP, Command, String
+
 from txdav.caldav.datastore.util import migrateHome as migrateCalendarHome
 from txdav.carddav.datastore.util import migrateHome as migrateAddressbookHome
 from txdav.common.datastore.file import CommonDataStore as FileStore, TOPPATHS
 from txdav.base.propertystore.xattr import PropertyStore as XattrPropertyStore
-from txdav.base.propertystore.appledouble_xattr import (
-    PropertyStore as AppleDoubleStore)
+from txdav.base.propertystore.appledouble_xattr import (PropertyStore
+                                                        as AppleDoubleStore)
 
+
+homeTypeLookup = {
+    "calendar": (migrateCalendarHome,
+                 lambda txn: txn.calendarHomeWithUID),
+    "addressbook": (migrateAddressbookHome,
+                    lambda txn: txn.addressbookHomeWithUID)
+}
+
+
+def swapAMP(oldAMP, newAMP):
+    """
+    Swap delivery of messages from an old L{AMP} instance to a new one.
+
+    This is useful for implementors of L{StoreSpawnerService} since they will
+    typically want to create one protocol for initializing the store, and
+    another for processing application commands.
+
+    @param oldAMP: An AMP instance currently hooked up to a transport, whose
+        job is done and wants to stop receiving messages.
+
+    @param newAMP: An AMP instance who wants to take over and start receiving
+        messages previously destined for oldAMP.
+
+    @return: C{newAMP}
+    """
+    oldAMP.boxReceiver = newAMP
+    newAMP.startReceivingBoxes(oldAMP)
+    return newAMP
+
+
+
+class StoreSpawnerService(SpawnerService):
+    """
+    Abstract subclass of L{SpawnerService} that describes how to spawn a subclass.
+    """
+
+    def spawnWithStore(self, here, there):
+        """
+        Like L{SpawnerService.spawn}, but instead of instantiating C{there}
+        with 0 arguments, it instantiates it with an L{ICalendarStore} /
+        L{IAddressbookStore}.
+        """
+        raise NotImplementedError("subclasses must implement the specifics")
+
+
+
+class Configure(Command):
+    """
+    Configure the upgrade helper process.
+    """
+
+    arguments = [("filename", String())]
+
+
+    
+class OneUpgrade(Command):
+    """
+    Upgrade a single calendar home.
+    """
+
+    arguments = [("uid", String()),
+                 ("homeType", String())]
+
+
+
+class LogIt(Command):
+    """
+    Log a message.
+    """
+    arguments = [("message", String())]
+
+
+
+class UpgradeDriver(AMP):
+    """
+    Helper protocol which runs in the master process doing the upgrade.
+    """
+
+    def __init__(self, upgradeService):
+        super(UpgradeDriver, self).__init__()
+        self.service = upgradeService
+
+
+    def configure(self, filename):
+        """
+        Configure the subprocess to examine the file store at the given path
+        name.
+        """
+        return self.callRemote(Configure, filename=filename)
+
+
+    def oneUpgrade(self, uid, homeType):
+        """
+        Upgrade one calendar or addressbook home, with the given uid of the
+        given type, and return a L{Deferred} which will fire when the upgrade
+        is complete.
+        """
+        return self.callRemote(OneUpgrade, uid=uid, homeType=homeType)
+
+
+
+class UpgradeHelperProcess(AMP):
+    """
+    Helper protocol which runs in a subprocess to upgrade.
+    """
+
+    def __init__(self, store):
+        """
+        Create with a reference to an SQL store.
+        """
+        super(UpgradeHelperProcess, self).__init__()
+        self.store = store
+        self.store.setMigrating(True)
+        
+
+    @Configure.responder
+    def configure(self, filename):
+        subsvc = None
+        self.upgrader = UpgradeToDatabaseService.wrapService(
+            CachingFilePath(filename), subsvc, self.store
+        )
+        return {}
+
+
+    @OneUpgrade.responder
+    def oneUpgrade(self, uid, homeType):
+        """
+        Upgrade one calendar home.
+        """
+        migrateFunc, destFunc = homeTypeLookup[homeType]
+        fileTxn = self.upgrader.fileStore.newTransaction()
+        return (
+            maybeDeferred(destFunc(fileTxn), uid)
+            .addCallback(
+                lambda fileHome:
+                self.upgrader.migrateOneHome(fileTxn, homeType, fileHome)
+            )
+            .addCallback(lambda ignored: {})
+        )
+
+
+
 class UpgradeToDatabaseService(Service, LoggingMixIn, object):
     """
     Upgrade resources from a filesystem store to a database store.
     """
 
     @classmethod
-    def wrapService(cls, path, service, store, uid=None, gid=None):
+    def wrapService(cls, path, service, store, uid=None, gid=None,
+                    parallel=0, spawner=None):
         """
         Create an L{UpgradeToDatabaseService} if there are still file-based
         calendar or addressbook homes remaining in the given path.
@@ -62,6 +212,12 @@
 
         @type service: L{IService}
 
+        @param parallel: The number of parallel subprocesses that should manage
+            the upgrade.
+
+        @param spawner: a concrete L{StoreSpawnerService} subclass that will be
+            used to spawn helper processes.
+
         @return: a service
         @rtype: L{IService}
         """
@@ -103,12 +259,14 @@
                     FileStore(path, None, True, True,
                               propertyStoreClass=appropriateStoreClass),
                     store, service, uid=uid, gid=gid,
+                    parallel=parallel, spawner=spawner,
                 )
                 return self
         return service
 
 
-    def __init__(self, fileStore, sqlStore, service, uid=None, gid=None):
+    def __init__(self, fileStore, sqlStore, service, uid=None, gid=None,
+                 parallel=0, spawner=None):
         """
         Initialize the service.
         """
@@ -117,9 +275,40 @@
         self.sqlStore = sqlStore
         self.uid = uid
         self.gid = gid
+        self.parallel = parallel
+        self.spawner = spawner
 
 
     @inlineCallbacks
+    def migrateOneHome(self, fileTxn, homeType, fileHome):
+        """
+        Migrate an individual calendar or addressbook home.
+        """
+        migrateFunc, destFunc = homeTypeLookup.get(homeType)
+        uid = fileHome.uid()
+        self.log_warn("Starting migration transaction %s UID %r" %
+                      (homeType, uid))
+        sqlTxn = self.sqlStore.newTransaction()
+        homeGetter = destFunc(sqlTxn)
+        if (yield homeGetter(uid, create=False)) is not None:
+            self.log_warn(
+                "%s home %r already existed not migrating" % (
+                    homeType, uid))
+            yield sqlTxn.abort()
+            yield fileTxn.commit()
+            returnValue(None)
+        sqlHome = yield homeGetter(uid, create=True)
+        yield migrateFunc(fileHome, sqlHome)
+        yield fileTxn.commit()
+        yield sqlTxn.commit()
+        # Remove file home after migration. FIXME: instead, this should be a
+        # public remove...HomeWithUID() API for de-provisioning.  (If we had
+        # this, this would simply be a store-to-store migrator rather than a
+        # filesystem-to-database upgrade.)
+        fileHome._path.remove()
+
+
+    @inlineCallbacks
     def doMigration(self):
         """
         Do the migration.  Called by C{startService}, but a different method
@@ -127,43 +316,61 @@
 
         @return: a Deferred which fires when the migration is complete.
         """
-        self.log_warn("Beginning filesystem -> database upgrade.")
-
         self.sqlStore.setMigrating(True)
+        parallel = self.parallel
+        if parallel:
+            self.log_warn("Starting %d upgrade helper processes." %
+                          (parallel,))
+            spawner = self.spawner
+            spawner.startService()
+            drivers = []
+            for value in xrange(parallel):
+                driver = yield spawner.spawnWithStore(UpgradeDriver(self),
+                                                      UpgradeHelperProcess)
+                drivers.append(driver)
 
-        for homeType, migrateFunc, eachFunc, destFunc, _ignore_topPathName in [
-            ("calendar", migrateCalendarHome,
-                self.fileStore.eachCalendarHome,
-                lambda txn: txn.calendarHomeWithUID,
-                "calendars"),
-            ("addressbook", migrateAddressbookHome,
-                self.fileStore.eachAddressbookHome,
-                lambda txn: txn.addressbookHomeWithUID,
-                "addressbooks")
+            # Wait for all subprocesses to be fully configured before
+            # continuing, but let them configure in any order.
+            self.log_warn("Configuring upgrade helper processes.")
+            yield DeferredList([driver.configure(self.fileStore._path.path)
+                                for driver in drivers])
+            self.log_warn("Upgrade helpers ready.")
+
+        self.log_warn("Beginning filesystem -> database upgrade.")
+        inParallel = []
+        for homeType, eachFunc in [
+                ("calendar", self.fileStore.eachCalendarHome),
+                ("addressbook", self.fileStore.eachAddressbookHome),
             ]:
             for fileTxn, fileHome in eachFunc():
                 uid = fileHome.uid()
                 self.log_warn("Migrating %s UID %r" % (homeType, uid))
-                sqlTxn = self.sqlStore.newTransaction()
-                homeGetter = destFunc(sqlTxn)
-                if (yield homeGetter(uid, create=False)) is not None:
-                    self.log_warn(
-                        "%s home %r already existed not migrating" % (
-                            homeType, uid))
-                    yield sqlTxn.abort()
+                if parallel:
+                    # No-op transaction here: make sure everything's unlocked
+                    # before asking the subprocess to handle it.
                     yield fileTxn.commit()
-                    continue
-                sqlHome = yield homeGetter(uid, create=True)
-                if sqlHome is None:
-                    raise RuntimeError("THIS SHOULD NOT BE POSSIBLE.")
-                yield migrateFunc(fileHome, sqlHome)
-                yield fileTxn.commit()
-                yield sqlTxn.commit()
-                # FIXME: need a public remove...HomeWithUID() for de-
-                # provisioning
+                    if not drivers:
+                        # All the subprocesses are currently busy processing an
+                        # upgrade.  Wait for one to become available.
+                        yield DeferredList(inParallel, fireOnOneCallback=True,
+                                           fireOnOneErrback=True)
+                    busy = drivers.pop(0)
+                    d = busy.oneUpgrade(fileHome.uid(), homeType)
+                    inParallel.append(d)
+                    def freeUp(result, d=d, busy=busy, uid=uid,
+                               homeType=homeType):
+                        inParallel.remove(d)
+                        drivers.append(busy)
+                        self.log_warn("Completed migration of %s uid %r" %
+                                      (homeType, uid))
+                        return result
+                    d.addBoth(freeUp)
+                else:
+                    yield self.migrateOneHome(fileTxn, homeType, fileHome)
 
-                # Remove file home after migration
-                fileHome._path.remove()
+        if inParallel:
+            yield DeferredList(inParallel)
+
         for homeType in TOPPATHS:
             homesPath = self.fileStore._path.child(homeType)
             if homesPath.isdir():
@@ -180,15 +387,21 @@
             for fp in sqlAttachmentsPath.walk():
                 os.chown(fp.path, uid, gid)
 
-        self.sqlStore.setMigrating(False) 
+        self.sqlStore.setMigrating(False)
+
+        if parallel:
+            self.log_warn("Stopping upgrade helper processes.")
+            yield spawner.stopService()
+            self.log_warn("Upgrade helpers all stopped.")
         self.log_warn(
             "Filesystem upgrade complete, launching database service."
         )
+        wrapped = self.wrappedService
+        if wrapped is not None:
+            # see http://twistedmatrix.com/trac/ticket/4649
+            reactor.callLater(0, wrapped.setServiceParent, self.parent)
 
-        # see http://twistedmatrix.com/trac/ticket/4649
-        reactor.callLater(0, self.wrappedService.setServiceParent, self.parent)
 
-
     def startService(self):
         """
         Start the service.

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py	2011-12-03 08:40:33 UTC (rev 8400)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py	2011-12-03 08:43:59 UTC (rev 8401)
@@ -23,6 +23,8 @@
 from twisted.application.service import Service, MultiService
 from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
 from twisted.internet.protocol import Protocol
+from twisted.protocols.amp import AMP, Command, String
+from twisted.python.reflect import qual, namedAny
 from twisted.trial.unittest import TestCase
 from txdav.caldav.datastore.test.common import CommonTests
 from txdav.carddav.datastore.test.common import CommonTests as ABCommonTests
@@ -30,13 +32,68 @@
 from txdav.common.datastore.test.util import theStoreBuilder, \
     populateCalendarsFrom, StubNotifierFactory, resetCalendarMD5s,\
     populateAddressBooksFrom, resetAddressBookMD5s
-from txdav.common.datastore.upgrade.migrate import UpgradeToDatabaseService
 
+from txdav.common.datastore.test.util import SQLStoreBuilder
+from txdav.common.datastore.upgrade.migrate import UpgradeToDatabaseService, \
+    StoreSpawnerService, swapAMP
+
+
+
+class CreateStore(Command):
+    """
+    Create a store in a subprocess.
+    """
+    arguments = [('delegateTo', String())]
+
+
+
+class StoreCreator(AMP):
+    """
+    Helper protocol.
+    """
+
+    @CreateStore.responder
+    def createStore(self, delegateTo):
+        """
+        Create a store and pass it to the named delegate class.
+        """
+        swapAMP(self, namedAny(delegateTo)(SQLStoreBuilder.childStore()))
+        return {}
+
+
+
+class StubSpawner(StoreSpawnerService):
+    """
+    Stub spawner service which populates the store forcibly.
+    """
+
+    @inlineCallbacks
+    def spawnWithStore(self, here, there):
+        """
+        'here' and 'there' are the helper protocols; in a slight modification
+        of the signature, 'there' will expect to be created with an instance of
+        a store.
+        """
+        master = yield self.spawn(AMP(), StoreCreator)
+        yield master.callRemote(CreateStore, delegateTo=qual(there))
+        returnValue(swapAMP(master, here))
+
+
+
 class HomeMigrationTests(TestCase):
     """
     Tests for L{UpgradeToDatabaseService}.
     """
 
+    def createUpgradeService(self):
+        """
+        Create an upgrade service.
+        """
+        return UpgradeToDatabaseService(
+            self.fileStore, self.sqlStore, self.stubService
+        )
+
+
     @inlineCallbacks
     def setUp(self):
         """
@@ -59,9 +116,7 @@
                 subStarted.callback(None)
         self.stubService = StubService()
         self.topService = MultiService()
-        self.upgrader = UpgradeToDatabaseService(
-            fileStore, self.sqlStore, self.stubService
-        )
+        self.upgrader = self.createUpgradeService()
         self.upgrader.setServiceParent(self.topService)
 
         requirements = CommonTests.requirements
@@ -216,3 +271,21 @@
         ):
             object = (yield adbk.addressbookObjectWithName(name))
             self.assertEquals(object.md5(), md5)
+
+
+
+class ParallelHomeMigrationTests(HomeMigrationTests):
+    """
+    Tests for home migrations running in parallel.  Functionally this should be
+    the same, so it's just a store created slightly differently.
+    """
+
+    def createUpgradeService(self):
+        """
+        Create an upgrade service.
+        """
+        return UpgradeToDatabaseService(
+            self.fileStore, self.sqlStore, self.stubService,
+            parallel=2, spawner=StubSpawner()
+        )
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111203/23452248/attachment-0001.html>


More information about the calendarserver-changes mailing list