def test_retry_database_read_errors( self, database_type: DatabaseType, caplog: LogCaptureFixture, monkeypatch: MonkeyPatch ) -> None: caplog.set_level(logging.DEBUG) database = Database(database_type) mocked_query = Mock(side_effect=[pyodbc.OperationalError("Error 1"), pyodbc.OperationalError("Error 2"), True]) monkeypatch.setattr(Session, "query", mocked_query) @retry_database_read_errors def query_with_retry() -> bool: with database.transaction_context() as session: return session.query() # type: ignore assert query_with_retry() assert mocked_query.call_count == 3 assert ( "Error 'Error 1' occurred while running query_with_retry. Trying again 2 more time(s) in 1 second(s)." in caplog.messages ) assert f"Error during {database} operation, transaction was rolled-back: Error 1" in caplog.messages assert ( "Error 'Error 2' occurred while running query_with_retry. Trying again 1 more time(s) in 1 second(s)." in caplog.messages ) assert f"Error during {database} operation, transaction was rolled-back: Error 2" in caplog.messages
async def _run_operation(self, func, *args, **kwargs): # execute func in thread pool of attached to cursor connection if not self._conn: raise pyodbc.OperationalError('Cursor is closed.') try: result = await self._conn._execute(func, *args, **kwargs) return result except pyodbc.Error as e: if self._conn and _is_conn_close_error(e): await self._conn.close() raise
def test_error_10004_logging(self, m_log): cursor = self.get_cursor() m_execute = mock.Mock() m_execute.side_effect = pyodbc.OperationalError() with mock.patch.object(cursor, 'execute', m_execute): sql = 'SELECT count(*) AS count_1' params = (1, 2, 3) with pytest.raises(pyodbc.OperationalError): self.__dialect__.do_execute(cursor, sql, params) m_log.error.assert_called_once_with( 'pyodbc OperationalError. Full statement: SELECT count(*) AS count_1' '\n Params: (1, 2, 3)')
def test_conexion_fallida(): # Given driver = 'dummy_driver' servidor = 'dummy_servidor' base_datos = 'dummy_base_datos' usuario = 'dummy_usuario' contraseña = 'dummy_contraseña' pyodbc.connect = Mock(side_effect=pyodbc.OperationalError('error code')) # When with pytest.raises(pyodbc.OperationalError) as error: ClienteBaseDatos(driver, servidor, base_datos, usuario, contraseña) # Then pyodbc.connect.assert_called_once() pyodbc.connect.assert_called_with("DRIVER={dummy_driver};SERVER=dummy_servidor;DATABASE=dummy_base_datos;UID=dummy_usuario;PWD=dummy_contraseña") assert error.value.args[0] == 'error code'
def test_retry_and_fail( self, database_type: DatabaseType, monkeypatch: MonkeyPatch, caplog: LogCaptureFixture ) -> None: monkeypatch.setattr(master_config, "db_connection_attempts", 3) monkeypatch.setattr(master_config, "db_connection_retry_sleep_seconds", 0.01) monkeypatch.setattr(pyodbc, "connect", Mock(side_effect=pyodbc.OperationalError("Always fails!"))) with pytest.raises(DatabaseConnectionFailure): with caplog.at_level(logging.DEBUG): database = Database(database_type) database.get_existing_table_names() # Run something that explicitly connects to the database assert caplog.messages == [ f"Trying to connect to {database_type.name} database, attempt #1", f"Unsuccessful attempt #1 to connect to {database_type.name} database: Always fails!", "Waiting 0.01 seconds before next connection attempt", f"Trying to connect to {database_type.name} database, attempt #2", f"Unsuccessful attempt #2 to connect to {database_type.name} database: Always fails!", "Waiting 0.01 seconds before next connection attempt", f"Trying to connect to {database_type.name} database, attempt #3", f"Unsuccessful attempt #3 to connect to {database_type.name} database: Always fails!", f"Unable to connect to {database_type.name} database after 3 attempt(s).", ]
def test_retry_database_read_errors_can_be_disabled( self, database_type: DatabaseType, caplog: LogCaptureFixture, monkeypatch: MonkeyPatch ) -> None: caplog.set_level(logging.DEBUG) database = Database(database_type) mocked_query = Mock(side_effect=[pyodbc.OperationalError("Error 1")]) monkeypatch.setattr(Session, "query", mocked_query) monkeypatch.setattr(master_config, "db_read_retries", 0) @retry_database_read_errors def query_with_retry() -> bool: with database.transaction_context() as session: return session.query() # type: ignore with pytest.raises(pyodbc.OperationalError, match="Error 1"): assert query_with_retry() assert mocked_query.call_count == 1 assert not ( "Error 'Error 1' occurred while running query_with_retry. Trying again 1 more time(s) in 1 second(s)." in caplog.messages ) assert f"Error during {database} operation, transaction was rolled-back: Error 1" in caplog.messages
def _run_operation(self, func, *args, **kwargs): # execute func in thread pool of attached to cursor connection if not self._conn: raise pyodbc.OperationalError('Cursor is closed.') future = self._conn._execute(func, *args, **kwargs) return future
def fail_twice_then_succeed(connection_string: str, timeout: float) -> pyodbc.Connection: nonlocal attempt if attempt < 2: attempt += 1 raise pyodbc.OperationalError(f"Test Fail {attempt}") return real_connect(connection_string, timeout=timeout)