class TransactionManager(object): def __init__(self): from ZODB.utils import WeakSet self._txn = None self._synchs = WeakSet() def begin(self): if self._txn is not None: self._txn.abort() txn = self._txn = Transaction(self._synchs, self) _new_transaction(txn, self._synchs) return txn def get(self): if self._txn is None: self._txn = Transaction(self._synchs, self) return self._txn def free(self, txn): assert txn is self._txn self._txn = None def registerSynch(self, synch): self._synchs.add(synch) def unregisterSynch(self, synch): self._synchs.remove(synch) def commit(self, sub=_marker): if sub is _marker: sub = None else: from ZODB.utils import deprecated37 deprecated37("subtransactions are deprecated; use " "transaction.savepoint() instead of " "transaction.commit(1)") return self.get().commit(sub, deprecation_wng=False) def abort(self, sub=_marker): if sub is _marker: sub = None else: from ZODB.utils import deprecated37 deprecated37("subtransactions are deprecated; use " "sp.rollback() instead of " "transaction.abort(1), where `sp` is the " "corresponding savepoint captured earlier") return self.get().abort(sub, deprecation_wng=False) def savepoint(self, optimistic=False): return self.get().savepoint(optimistic)
class TransactionManager(object): def __init__(self): self._txn = None self._synchs = WeakSet() def begin(self): if self._txn is not None: self._txn.abort() txn = self._txn = Transaction(self._synchs, self) _new_transaction(txn, self._synchs) return txn def get(self): if self._txn is None: self._txn = Transaction(self._synchs, self) return self._txn def free(self, txn): assert txn is self._txn self._txn = None def registerSynch(self, synch): self._synchs.add(synch) def unregisterSynch(self, synch): self._synchs.remove(synch) def isDoomed(self): return self.get().isDoomed() def doom(self): return self.get().doom() def commit(self): return self.get().commit() def abort(self): return self.get().abort() def savepoint(self, optimistic=False): return self.get().savepoint(optimistic)
class _ConnectionPool(object): """Manage a pool of connections. CAUTION: Methods should be called under the protection of a lock. This class does no locking of its own. There's no limit on the number of connections this can keep track of, but a warning is logged if there are more than pool_size active connections, and a critical problem if more than twice pool_size. New connections are registered via push(). This will log a message if "too many" connections are active. When a connection is explicitly closed, tell the pool via repush(). That adds the connection to a stack of connections available for reuse, and throws away the oldest stack entries if the stack is too large. pop() pops this stack. When a connection is obtained via pop(), the pool holds only a weak reference to it thereafter. It's not necessary to inform the pool if the connection goes away. A connection handed out by pop() counts against pool_size only so long as it exists, and provided it isn't repush()'ed. A weak reference is retained so that DB methods like connectionDebugInfo() can still gather statistics. """ def __init__(self, pool_size): # The largest # of connections we expect to see alive simultaneously. self.pool_size = pool_size # A weak set of all connections we've seen. A connection vanishes # from this set if pop() hands it out, it's not reregistered via # repush(), and it becomes unreachable. self.all = WeakSet() # A stack of connections available to hand out. This is a subset # of self.all. push() and repush() add to this, and may remove # the oldest available connections if the pool is too large. # pop() pops this stack. There are never more than pool_size entries # in this stack. # In Python 2.4, a collections.deque would make more sense than # a list (we push only "on the right", but may pop from both ends). self.available = [] # Change our belief about the expected maximum # of live connections. # If the pool_size is smaller than the current value, this may discard # the oldest available connections. def set_pool_size(self, pool_size): self.pool_size = pool_size self._reduce_size() # Register a new available connection. We must not know about c already. # c will be pushed onto the available stack even if we're over the # pool size limit. def push(self, c): assert c not in self.all assert c not in self.available self._reduce_size(strictly_less=True) self.all.add(c) self.available.append(c) n, limit = len(self.all), self.pool_size if n > limit: reporter = logger.warn if n > 2 * limit: reporter = logger.critical reporter("DB.open() has %s open connections with a pool_size " "of %s", n, limit) # Reregister an available connection formerly obtained via pop(). This # pushes it on the stack of available connections, and may discard # older available connections. def repush(self, c): assert c in self.all assert c not in self.available self._reduce_size(strictly_less=True) self.available.append(c) # Throw away the oldest available connections until we're under our # target size (strictly_less=False) or no more than that (strictly_less= # True, the default). def _reduce_size(self, strictly_less=False): target = self.pool_size - bool(strictly_less) while len(self.available) > target: c = self.available.pop(0) self.all.remove(c) # While application code may still hold a reference to `c`, # there's little useful that can be done with this Connection # anymore. Its cache may be holding on to limited resources, # and we replace the cache with an empty one now so that we # don't have to wait for gc to reclaim it. Note that it's not # possible for DB.open() to return `c` again: `c` can never # be in an open state again. # TODO: Perhaps it would be better to break the reference # cycles between `c` and `c._cache`, so that refcounting reclaims # both right now. But if user code _does_ have a strong # reference to `c` now, breaking the cycle would not reclaim `c` # now, and `c` would be left in a user-visible crazy state. c._resetCache() # Pop an available connection and return it, or return None if none are # available. In the latter case, the caller should create a new # connection, register it via push(), and call pop() again. The # caller is responsible for serializing this sequence. def pop(self): result = None if self.available: result = self.available.pop() # Leave it in self.all, so we can still get at it for statistics # while it's alive. assert result in self.all return result # For every live connection c, invoke f(c). def map(self, f): self.all.map(f)
class _ConnectionPool(object): """Manage a pool of connections. CAUTION: Methods should be called under the protection of a lock. This class does no locking of its own. There's no limit on the number of connections this can keep track of, but a warning is logged if there are more than pool_size active connections, and a critical problem if more than twice pool_size. New connections are registered via push(). This will log a message if "too many" connections are active. When a connection is explicitly closed, tell the pool via repush(). That adds the connection to a stack of connections available for reuse, and throws away the oldest stack entries if the stack is too large. pop() pops this stack. When a connection is obtained via pop(), the pool holds only a weak reference to it thereafter. It's not necessary to inform the pool if the connection goes away. A connection handed out by pop() counts against pool_size only so long as it exists, and provided it isn't repush()'ed. A weak reference is retained so that DB methods like connectionDebugInfo() can still gather statistics. """ def __init__(self, pool_size): # The largest # of connections we expect to see alive simultaneously. self.pool_size = pool_size # A weak set of all connections we've seen. A connection vanishes # from this set if pop() hands it out, it's not reregistered via # repush(), and it becomes unreachable. self.all = WeakSet() # A stack of connections available to hand out. This is a subset # of self.all. push() and repush() add to this, and may remove # the oldest available connections if the pool is too large. # pop() pops this stack. There are never more than pool_size entries # in this stack. # In Python 2.4, a collections.deque would make more sense than # a list (we push only "on the right", but may pop from both ends). self.available = [] # Change our belief about the expected maximum # of live connections. # If the pool_size is smaller than the current value, this may discard # the oldest available connections. def set_pool_size(self, pool_size): self.pool_size = pool_size self._reduce_size() # Register a new available connection. We must not know about c already. # c will be pushed onto the available stack even if we're over the # pool size limit. def push(self, c): assert c not in self.all assert c not in self.available self._reduce_size(strictly_less=True) self.all.add(c) self.available.append(c) n, limit = len(self.all), self.pool_size if n > limit: reporter = logger.warn if n > 2 * limit: reporter = logger.critical reporter("DB.open() has %s open connections with a pool_size " "of %s", n, limit) # Reregister an available connection formerly obtained via pop(). This # pushes it on the stack of available connections, and may discard # older available connections. def repush(self, c): assert c in self.all assert c not in self.available self._reduce_size(strictly_less=True) self.available.append(c) # Throw away the oldest available connections until we're under our # target size (strictly_less=False) or no more than that (strictly_less= # True, the default). def _reduce_size(self, strictly_less=False): target = self.pool_size - bool(strictly_less) while len(self.available) > target: c = self.available.pop(0) self.all.remove(c) # While application code may still hold a reference to `c`, # there's little useful that can be done with this Connection # anymore. Its cache may be holding on to limited resources, # and we replace the cache with an empty one now so that we # don't have to wait for gc to reclaim it. Note that it's not # possible for DB.open() to return `c` again: `c` can never # be in an open state again. # TODO: Perhaps it would be better to break the reference # cycles between `c` and `c._cache`, so that refcounting reclaims # both right now. But if user code _does_ have a strong # reference to `c` now, breaking the cycle would not reclaim `c` # now, and `c` would be left in a user-visible crazy state. c._resetCache() # Pop an available connection and return it, or return None if none are # available. In the latter case, the caller should create a new # connection, register it via push(), and call pop() again. The # caller is responsible for serializing this sequence. def pop(self): result = None if self.available: result = self.available.pop() # Leave it in self.all, so we can still get at it for statistics # while it's alive. assert result in self.all return result def map(self, f, open_connections=True): """For every live connection c, invoke f(c). If `open_connections` is false then only call f(c) on closed connections. """ if open_connections: self.all.map(f) else: map(f, self.available)