diff options
author | Ilya Shakhat <shakhat@gmail.com> | 2017-07-25 12:18:37 +0200 |
---|---|---|
committer | Ilya Shakhat <shakhat@gmail.com> | 2017-08-03 17:59:32 +0200 |
commit | d77af64d8e5ba21b773de7666c8de0db89db68d4 (patch) | |
tree | f2dd8406ab7fae19974b1cbc956b31dbfb056db2 | |
parent | 2af0348d26f69e4be5c5af4989782a0c78b1aba9 (diff) | |
download | oslo-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.py | 4 | ||||
-rw-r--r-- | oslo_db/tests/sqlalchemy/test_exc_filters.py | 33 |
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 + ) |