From de027d0fba165c63147a868bf6f1ff4843e566f4 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 8 Feb 2016 22:57:02 -0500 Subject: [PATCH] - Testing reveals that we have *no* weak references to any cx_Oracle connections at all, yet cx_Oracle still has open sessions that cannot be killed until process dies. Oracle wins! Add a completely separate DB reaper script that runs after py.test is done. (cherry picked from commit 11a8440bc43aa9f7eb6f2cb7b7f43e0cf6680f41) --- lib/sqlalchemy/testing/provision.py | 35 ++++++++++++++++++++++++++--- reap_oracle_dbs.py | 24 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 reap_oracle_dbs.py diff --git a/lib/sqlalchemy/testing/provision.py b/lib/sqlalchemy/testing/provision.py index b998b7f786..b928d006d3 100644 --- a/lib/sqlalchemy/testing/provision.py +++ b/lib/sqlalchemy/testing/provision.py @@ -4,6 +4,8 @@ from sqlalchemy import exc from sqlalchemy.util import compat from . import config, engines import time +import logging +log = logging.getLogger(__name__) FOLLOWER_IDENT = None @@ -229,12 +231,39 @@ def _oracle_configure_follower(config, ident): config.test_schema_2 = "%s_ts2" % ident +def _ora_drop_ignore(conn, dbname): + try: + conn.execute("drop user %s cascade" % dbname) + log.info("Reaped db: %s" % dbname) + return True + except exc.DatabaseError as err: + log.warn("couldn't drop db: %s" % err) + return False + @_drop_db.for_db("oracle") def _oracle_drop_db(cfg, eng, ident): with eng.connect() as conn: - conn.execute("drop user %s cascade" % ident) - conn.execute("drop user %s_ts1 cascade" % ident) - conn.execute("drop user %s_ts2 cascade" % ident) + # cx_Oracle seems to occasionally leak open connections when a large + # suite it run, even if we confirm we have zero references to + # connection objects. + # while there is a "kill session" command in Oracle, + # it unfortunately does not release the connection sufficiently. + _ora_drop_ignore(conn, ident) + _ora_drop_ignore(conn, "%s_ts1" % ident) + _ora_drop_ignore(conn, "%s_ts2" % ident) + +def reap_oracle_dbs(eng): + log.info("Reaping Oracle dbs...") + with eng.connect() as conn: + to_reap = conn.execute( + "select u.username from all_users u where username " + "like 'TEST_%' and not exists (select username " + "from v$session where username=u.username)") + dropped = 0 + for total, (username, ) in enumerate(to_reap, 1): + if _ora_drop_ignore(conn, username): + dropped += 1 + log.info("Dropped %d out of %d stale databases detected", dropped, total) @_follower_url_from_main.for_db("oracle") diff --git a/reap_oracle_dbs.py b/reap_oracle_dbs.py new file mode 100644 index 0000000000..ff638a01ed --- /dev/null +++ b/reap_oracle_dbs.py @@ -0,0 +1,24 @@ +"""Drop Oracle databases that are left over from a +multiprocessing test run. + +Currently the cx_Oracle driver seems to sometimes not release a +TCP connection even if close() is called, which prevents the provisioning +system from dropping a database in-process. + +""" +from sqlalchemy.testing.plugin import plugin_base +from sqlalchemy.testing import engines +from sqlalchemy.testing import provision +import logging + +logging.basicConfig() +logging.getLogger(provision.__name__).setLevel(logging.INFO) + +plugin_base.read_config() +oracle = plugin_base.file_config.get('db', 'oracle') +from sqlalchemy.testing import provision + +engine = engines.testing_engine(oracle, {}) +provision.reap_oracle_dbs(engine) + + -- 2.47.3