Description
- What versions are you using?
DB: Oracle Database 12c Standard Edition Release 12.1.0.2.0
platform.platform: Linux-4.18.0-348.12.2.el8_5.x86_64-x86_64-with-glibc2.28
sys.maxsize > 2**32: True
platform.python_version: 3.10.0
cx_Oracle.version: 8.3.0
cx_Oracle.clientversion: (18, 5, 0, 0, 0)
- Describe the problem
After we performed an upgrade to various software because of a switch from python 2.7 to 3.X we noticed that under heavy load in a multi-threaded application we were having a lot more active connections and increased execution time.
Eliminating our application from the equation shows slowdown when going from single-threaded to multi-threaded with a simple python script (below)
The following are some rough timings on RHEL 8.5 with python 3.10 and cxOracle 8.3.0 when performing 100k queries on a single thread vs distributed on 10 threads (10k per thread)
time /tmp/cx.py -t 1 -r 100000
real 1m35.427s
user 0m59.392s
sys 0m7.678s
time /tmp/cx.py -t 10 -r 10000
real 4m10.947s
user 3m26.608s
sys 5m30.610s
The table used here isn't anything special. There are about 200 entries only in it so most of the queries will return None anyway.
If I run the script on a RHEL 7.2 server (and target the same DB) I get better results compared to RHEL 8.
time /tmp/cx.py -t 10 -r 10000
real 1m46.939s
user 1m18.926s
sys 1m26.848s
Before this we were not seeing any problems with python 2.7 / cxOracle 7.3.0 under RHEL 6.X or 7.X.
If you need other data from our env to help narrow things down let me know (@mariustantareanu can also respond to questions if I am not available)
- Include a runnable Python script that shows the problem.
As I said, the table itself doesn't really matter. I used the following python script (with some strings for custom paths and user/passwords removed)
import os
import threading
import sys
import argparse
os.environ['TNS_ADMIN'] = '<>'
os.environ['ORA_TZFILE'] = '<>'
os.environ['ORA_NLS10'] = '<>'
import cx_Oracle
def f(con, start_id, num_reqs):
for i in range(num_reqs):
x = start_id + i
cursor = con.cursor()
try:
cursor.execute('select * from devices where dvc_id = :dvcId', dvcId=x)
d = cursor.fetchone()
finally:
cursor.close()
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--threads', help='Number of threads', type=int, required=True)
parser.add_argument('-r', '--requests', help='Number of requests per thread', type=int, required=True)
parser.add_argument('-u', '--username', default='<>')
parser.add_argument('-p', '--password', default='<>')
parser.add_argument('-d', '--dbname', default='<>')
args = parser.parse_args()
connect_string = '%s/%s@%s' % (args.username, args.password, args.dbname)
print("pid=%d" % os.getpid())
con_ts = []
for i in range(args.threads):
con = cx_Oracle.Connection(connect_string, threaded=True)
t = threading.Thread(target=f, args=(con, i * args.requests, args.requests))
con_ts.append((con, t))
for _, t in con_ts:
t.start()
for _, t in con_ts:
t.join()