Skip to content

Commit 705ca06

Browse files
authored
Make pconnect cache keys thread-specific (#1045)
* Make pconnect cache keys thread-specific Include thread id in the persistent connection cache key so pconnect entries are isolated per thread. This prevents accidental cross-thread reuse of cached connections. Add regression test for thread safety of persistent connections. * Fix PyUnicode_FromFormat reference leaks in pconnect cache key construction - Ensure PID and thread-id temporary unicode objects are NULL-checked and decref'd after PyUnicode_Concat. - Prevents incremental memory growth in long-running workloads using persistent connections.
1 parent 100df91 commit 705ca06

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

ibm_db.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,8 @@ static PyObject *_python_ibm_db_connect_helper(PyObject *self, PyObject *args, i
19211921
char server[2048];
19221922
int isNewBuffer = 0;
19231923
PyObject *pid = NULL;
1924+
PyObject *pidStr = NULL;
1925+
PyObject *threadIdStr = NULL;
19241926
conn_alive = 1;
19251927
if (!PyArg_ParseTuple(args, "OOO|OO", &databaseObj, &uidObj, &passwordObj, &options, &literal_replacementObj))
19261928
{
@@ -1977,9 +1979,24 @@ static PyObject *_python_ibm_db_connect_helper(PyObject *self, PyObject *args, i
19771979
}
19781980
snprintf(messageStr, sizeof(messageStr), "Obtain process id: %p", pid);
19791981
LogMsg(INFO, messageStr);
1980-
hKey = PyUnicode_Concat(hKey, PyUnicode_FromFormat("%ld", PyLong_AsLong(pid)));
1982+
pidStr = PyUnicode_FromFormat("%ld", PyLong_AsLong(pid));
1983+
if (pidStr == NULL)
1984+
{
1985+
Py_DECREF(pid);
1986+
return NULL;
1987+
}
1988+
hKey = PyUnicode_Concat(hKey, pidStr);
1989+
Py_DECREF(pidStr);
19811990
Py_DECREF(pid);
19821991

1992+
threadIdStr = PyUnicode_FromFormat("%ld", (long)PyThread_get_thread_ident());
1993+
if (threadIdStr == NULL)
1994+
{
1995+
return NULL;
1996+
}
1997+
hKey = PyUnicode_Concat(hKey, threadIdStr);
1998+
Py_DECREF(threadIdStr);
1999+
19832000
entry = PyDict_GetItem(persistent_list, hKey);
19842001

19852002
if (entry != NULL)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#
2+
# Licensed Materials - Property of IBM
3+
#
4+
# (c) Copyright IBM Corp. 2007-2008
5+
#
6+
7+
from __future__ import print_function
8+
import sys
9+
import threading
10+
import unittest
11+
import ibm_db
12+
import config
13+
from testfunctions import IbmDbTestFunctions
14+
15+
16+
class IbmDbTestCase(unittest.TestCase):
17+
18+
def test_6793_PconnectThreadIsolation(self):
19+
obj = IbmDbTestFunctions()
20+
obj.assert_expect(self.run_test_6793)
21+
22+
def run_test_6793(self):
23+
start_event = threading.Event()
24+
lock = threading.Lock()
25+
results = []
26+
errors = []
27+
28+
def worker():
29+
try:
30+
start_event.wait()
31+
32+
if sys.platform == 'zos':
33+
conn = ibm_db.pconnect(config.database, '', '')
34+
else:
35+
conn = ibm_db.pconnect(config.database, config.user, config.password)
36+
37+
if not conn:
38+
with lock:
39+
errors.append("connect_failed")
40+
return
41+
42+
is_active = ibm_db.active(conn)
43+
conn_id = id(conn)
44+
ibm_db.close(conn)
45+
46+
with lock:
47+
results.append((conn_id, is_active))
48+
except Exception:
49+
with lock:
50+
errors.append("worker_exception")
51+
52+
threads = [threading.Thread(target=worker) for _ in range(2)]
53+
54+
for t in threads:
55+
t.start()
56+
57+
start_event.set()
58+
59+
for t in threads:
60+
t.join()
61+
62+
if errors:
63+
print("errors:", len(errors))
64+
return
65+
66+
unique_conn_handles = len(set([conn_id for conn_id, _ in results]))
67+
active_count = sum(1 for _, is_active in results if is_active)
68+
69+
print("workers:", len(results))
70+
print("active_count:", active_count)
71+
print("unique_conn_handles:", unique_conn_handles)
72+
73+
74+
#__END__
75+
#__LUW_EXPECTED__
76+
#workers: 2
77+
#active_count: 2
78+
#unique_conn_handles: 2
79+
#__ZOS_EXPECTED__
80+
#workers: 2
81+
#active_count: 2
82+
#unique_conn_handles: 2
83+
#__SYSTEMI_EXPECTED__
84+
#workers: 2
85+
#active_count: 2
86+
#unique_conn_handles: 2
87+
#__IDS_EXPECTED__
88+
#workers: 2
89+
#active_count: 2
90+
#unique_conn_handles: 2

0 commit comments

Comments
 (0)