Skip to content

Commit ae5e98b

Browse files
committed
Try to fix the previous fix - once we've started working with a
connection in a transaction we can't just keep getting new connections, this would be nonsensical ...
1 parent c2fe721 commit ae5e98b

File tree

1 file changed

+26
-12
lines changed
  • src/Products/ZPsycopgDA

1 file changed

+26
-12
lines changed

src/Products/ZPsycopgDA/db.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class DB(TM):
4545
_p_oid = _p_changed = None
4646
_registered = False
4747
_sort_key = '1'
48+
_conn = None
4849

4950
def __init__(self, dsn, tilevel, typecasts, enc='utf-8'):
5051
self.dsn = dsn
@@ -59,6 +60,20 @@ def __init__(self, dsn, tilevel, typecasts, enc='utf-8'):
5960
self.make_mappings()
6061

6162
def getconn(self, init='ignored'):
63+
if self._conn:
64+
# As the TM is a short-lived object, keep a reference to the connection - we really
65+
# expect the pool's getconn() to reliably return the same connection anyway.
66+
# This is needed to differentiate between an initial connection
67+
# where its fine to keep going through the pool and fetching a new one
68+
# versus an existing connection in a running transaction where e.g. a failure
69+
# on the server side has aborted the transaction and we can't e.g. run `select 1`
70+
# any longer but we also need the original connection to clean up correctly within
71+
# Zope's transaction management.
72+
# Theoretically this could be placed in `_register()` but I'm not 100% sure
73+
# someone might be using getconn() without the TM integration thus going around
74+
# `_register()` ...
75+
return self._conn
76+
6277
_pool = pool.getpool(self.dsn, create=True)
6378

6479
# Loop to support cleaning up potentially all `maxconn` faulty
@@ -74,22 +89,21 @@ def getconn(self, init='ignored'):
7489
except (psycopg2.InterfaceError, psycopg2.OperationalError):
7590
pool.putconn(self.dsn, conn, close=True)
7691

77-
if id(conn) in _pool._initialized:
78-
return conn
92+
if id(conn) not in _pool._initialized:
93+
conn.set_session(isolation_level=int(self.tilevel))
94+
conn.set_client_encoding(self.encoding)
95+
for tc in self.typecasts:
96+
register_type(tc, conn)
97+
_pool._initialized.add(id(conn))
7998

80-
conn.set_session(isolation_level=int(self.tilevel))
81-
conn.set_client_encoding(self.encoding)
82-
for tc in self.typecasts:
83-
register_type(tc, conn)
84-
_pool._initialized.add(id(conn))
99+
self._conn = conn
85100
return conn
86101

87102
def putconn(self, close=False):
88-
try:
89-
conn = pool.getconn(self.dsn, False)
90-
except AttributeError:
91-
pass
92-
pool.putconn(self.dsn, conn, close)
103+
if not self._conn:
104+
return
105+
pool.putconn(self.dsn, self._conn, close)
106+
self._conn = None
93107

94108
def getcursor(self):
95109
conn = self.getconn()

0 commit comments

Comments
 (0)