summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Shakhat <shakhat@gmail.com>2017-07-25 12:18:37 +0200
committerIlya Shakhat <shakhat@gmail.com>2017-08-03 17:59:32 +0200
commitd77af64d8e5ba21b773de7666c8de0db89db68d4 (patch)
treef2dd8406ab7fae19974b1cbc956b31dbfb056db2
parent2af0348d26f69e4be5c5af4989782a0c78b1aba9 (diff)
downloadoslo-db-d77af64d8e5ba21b773de7666c8de0db89db68d4.tar.gz
Let others listen to SQLAlchemy errors
SQLAlchemy has 2 options of implementing error event listener. It can either raise exception directly or it can return exception into chain. With the first option all other handlers are ignored (ref: http://docs.sqlalchemy.org/en/latest/core/events.html?highlight=context#sqlalchemy.events.ConnectionEvents.handle_error) OSProfiler is interested to subscribe to SQLAlchemy error events. However with the current approach only Oslo.DB gets the events. Change-Id: Ia6bccd5af5f2e38f4333f1cc435f6bb9c12bc09f Related-Bug: 1706299
-rw-r--r--oslo_db/sqlalchemy/exc_filters.py4
-rw-r--r--oslo_db/tests/sqlalchemy/test_exc_filters.py33
2 files changed, 35 insertions, 2 deletions
diff --git a/oslo_db/sqlalchemy/exc_filters.py b/oslo_db/sqlalchemy/exc_filters.py
index 9803343..e42718b 100644
--- a/oslo_db/sqlalchemy/exc_filters.py
+++ b/oslo_db/sqlalchemy/exc_filters.py
@@ -475,11 +475,11 @@ def handler(context):
if isinstance(
dbe, exception.DBConnectionError):
context.is_disconnect = True
- raise
+ return dbe
def register_engine(engine):
- event.listen(engine, "handle_error", handler)
+ event.listen(engine, "handle_error", handler, retval=True)
@event.listens_for(engine, "rollback_savepoint")
def rollback_savepoint(conn, name, context):
diff --git a/oslo_db/tests/sqlalchemy/test_exc_filters.py b/oslo_db/tests/sqlalchemy/test_exc_filters.py
index ce148db..93ad770 100644
--- a/oslo_db/tests/sqlalchemy/test_exc_filters.py
+++ b/oslo_db/tests/sqlalchemy/test_exc_filters.py
@@ -1496,3 +1496,36 @@ class TestDBConnectPingWrapping(TestsExceptionFilter):
self.OperationalError('%d MySQL server has gone away' % code),
is_disconnect=False
)
+
+
+class TestsErrorHandler(TestsExceptionFilter):
+ def test_multiple_error_handlers(self):
+ handler = mock.MagicMock(return_value=None)
+ sqla.event.listen(self.engine, "handle_error", handler, retval=True)
+
+ # cause an error in DB API
+ self._run_test(
+ "mysql", "select you_made_a_programming_error",
+ self.ProgrammingError("Error 123, you made a mistake"),
+ exception.DBError
+ )
+
+ # expect custom handler to be called together with oslo.db's one
+ self.assertEqual(1, handler.call_count,
+ 'Custom handler should be called')
+
+ def test_chained_exceptions(self):
+ class CustomError(Exception):
+ pass
+
+ def handler(context):
+ return CustomError('Custom Error')
+
+ sqla.event.listen(self.engine, "handle_error", handler, retval=True)
+
+ # cause an error in DB API, expect exception from custom handler
+ self._run_test(
+ "mysql", "select you_made_a_programming_error",
+ self.ProgrammingError("Error 123, you made a mistake"),
+ CustomError
+ )