Commit 4e973aa1 authored by Chris McDonough's avatar Chris McDonough

Initial changes to support the use of multidatabases instead of Mount.py's...

Initial changes to support the use of multidatabases instead of Mount.py's homegrown mounting support.
parent c4783786
......@@ -17,7 +17,6 @@ $Id$
"""
import sys
from thread import allocate_lock
from ZODB.ActivityMonitor import ActivityMonitor
import Globals
......@@ -31,11 +30,10 @@ class DBTab:
def __init__(self, db_factories, mount_paths):
self._started = 0
self.opened = {} # { name -> Database instance }
self.lock = allocate_lock()
self.db_factories = db_factories # { name -> DatabaseFactory }
self.mount_paths = mount_paths # { virtual path -> name }
self.databases = {}
def startup(self):
"""Opens the databases set to open_at_startup."""
......@@ -76,45 +74,25 @@ class DBTab:
def getDatabase(self, mount_path=None, name=None, is_root=0):
"""Returns an opened database. Requires either mount_path or name.
"""
self.startup()
self.startup() # XXX get rid of this
if name is None:
if mount_path is None:
raise ValueError('Either mount_path or name is required')
name = self.mount_paths.get(mount_path)
if name is None:
self._mountPathError(mount_path)
db = self.opened.get(name)
name = self.getName(mount_path)
db = self.databases.get(name, None)
if db is None:
if not self.db_factories.has_key(name):
raise KeyError('%s is not a configured database' % repr(name))
self.lock.acquire()
try:
# Check again, since the database may have been created
# by another thread before the lock was acquired.
db = self.opened.get(name)
if db is None:
db = self._createDatabase(name, is_root)
finally:
self.lock.release()
factory = self.getDatabaseFactory(name=name)
db = factory.open(name, self.databases)
return db
def getDatabaseFactory(self, mount_path=None, name=None):
if name is None:
if mount_path is None:
raise ValueError('Either mount_path or name is required')
name = self.getName(mount_path)
if not self.db_factories.has_key(name):
raise KeyError('%s is not a configured database' % repr(name))
return self.db_factories[name]
def getName(self, mount_path):
name = self.mount_paths.get(mount_path)
if name is None:
self._mountPathError(mount_path)
return self.db_factories[name]
def _createDatabase(self, name, is_root):
factory = self.db_factories[name]
db = factory.open()
self.opened[name] = db
if not is_root:
Globals.opened.append(db)
# If it is the root database, Zope will add the database to
# Globals.opened. A database should not be listed twice.
return db
return name
......@@ -69,17 +69,9 @@ class MountPoint(Persistence.Persistent, Acquisition.Implicit):
def _getMountedConnection(self, anyjar):
db_name = self._getDBName()
conn = anyjar._getMountedConnection(db_name)
if conn is None:
root_conn = anyjar._getRootConnection()
if db_name == self._getRootDBName():
conn = root_conn
else:
conn = self._getDB().open(version=root_conn.getVersion())
root_conn._addMountedConnection(db_name, conn)
conn = anyjar.get_connection(db_name)
return conn
def _getOrOpenObject(self, parent):
t = self._v_data
if t is not None:
......@@ -143,95 +135,3 @@ class MountPoint(Persistence.Persistent, Acquisition.Implicit):
traceback.print_tb(exc[2], 100, f)
self._v_connect_error = (exc[0], exc[1], f.getvalue())
exc = None
class ConnectionPatches:
# Changes to Connection.py that might fold into ZODB
_root_connection = None
_mounted_connections = None
def _getRootConnection(self):
root_conn = self._root_connection
if root_conn is None:
return self
else:
return root_conn
def _getMountedConnection(self, name):
conns = self._getRootConnection()._mounted_connections
if conns is None:
return None
else:
return conns.get(name)
def _addMountedConnection(self, name, conn):
if conn._root_connection is not None:
raise ValueError, 'Connection %s is already mounted' % repr(conn)
root_conn = self._getRootConnection()
conns = root_conn._mounted_connections
if conns is None:
conns = {}
root_conn._mounted_connections = conns
if conns.has_key(name):
raise KeyError, 'A connection named %s already exists' % repr(name)
conn._root_connection = root_conn
conns[name] = conn
def _setDB(self, odb, *args, **kw):
self._real_setDB(odb, *args, **kw)
conns = self._mounted_connections
if conns:
for conn in conns.values():
conn._setDB(conn._db, *args, **kw)
def close(self):
if self._root_connection is not None:
raise RuntimeError("Should not close mounted connections directly")
conns = self._mounted_connections
if conns:
for conn in conns.values():
# Notify the activity monitor
db = conn.db()
f = getattr(db, 'getActivityMonitor', None)
if f is not None:
am = f()
if am is not None:
am.closedConnection(conn)
conn.cacheGC() # This is a good time to do some GC
# XXX maybe we ought to call the close callbacks.
conn._storage = conn._normal_storage = None
conn._savepoint_storage = None
conn.new_oid = conn._opened = None
conn._debug_info = ()
# collector #1350: ensure that the connection is unregistered
# from the transaction manager (XXX API method?)
if conn._synch:
conn.transaction_manager.unregisterSynch(conn)
# The mounted connection keeps a reference to
# its database, but nothing else.
# Note that mounted connections can not operate
# independently, so don't use _closeConnection() to
# return them to the pool. Only the root connection
# should be returned.
# Close this connection only after the mounted connections
# have been closed. Otherwise, this connection gets returned
# to the pool too early and another thread might use this
# connection before the mounted connections have all been
# closed.
self._real_close()
if 1:
# patch Connection.py.
from ZODB.Connection import Connection
Connection._real_setDB = Connection._setDB
Connection._real_close = Connection.close
for k, v in ConnectionPatches.__dict__.items():
if not k.startswith('__'):
setattr(Connection, k, v)
......@@ -107,7 +107,7 @@ class CustomTrailblazer (SimpleTrailblazer):
obj = context.unrestrictedTraverse(id)
# Commit a subtransaction to assign the new object to
# the correct database.
transaction.commit(1)
transaction.savepoint()
return obj
......@@ -133,6 +133,14 @@ class MountedObject(MountPoint, SimpleItem):
id = path.split('/')[-1]
MountPoint.__init__(self, id)
def _getMountedConnection(self, anyjar):
db_name = self._getDBName()
try:
conn = anyjar.get_connection(db_name)
except KeyError:
conn = self._getDB().open()
return conn
def mount_error_(self):
return self._v_connect_error
......@@ -177,7 +185,7 @@ class MountedObject(MountPoint, SimpleItem):
obj = Application()
root[real_root] = obj
# Get it into the database
transaction.commit(1)
transaction.savepoint()
else:
raise
......
......@@ -107,7 +107,7 @@ class DBTabTests (unittest.TestCase):
self.app._p_jar.close()
del self.app
del self.db
for db in self.conf.opened.values():
for db in self.conf.databases.values():
db.close()
del self.conf
......
......@@ -156,11 +156,11 @@ class ZopeDatabase(ZODBDatabase):
""" A ZODB database datatype that can handle an extended set of
attributes for use by DBTab """
def createDB(self):
return ZODBDatabase.open(self)
def createDB(self, database_name, databases):
return ZODBDatabase.open(self, database_name, databases)
def open(self):
DB = self.createDB()
def open(self, database_name, databases):
DB = self.createDB(database_name, databases)
if self.config.connection_class:
# set the connection class
DB.klass = self.config.connection_class
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment