mirror of
https://github.com/sprockets/sprockets-postgres.git
synced 2024-12-28 11:24:11 +00:00
Don't catch every psycopg2 exception in connector
This was swallowing exceptions downstream in the RequestHandler, like psycopg2.DataError
This commit is contained in:
parent
3dc8030c3e
commit
54a92667d1
2 changed files with 68 additions and 15 deletions
|
@ -340,26 +340,22 @@ class ApplicationMixin:
|
||||||
timeout=timeout) as cursor:
|
timeout=timeout) as cursor:
|
||||||
yield PostgresConnector(
|
yield PostgresConnector(
|
||||||
cursor, on_error, on_duration, timeout)
|
cursor, on_error, on_duration, timeout)
|
||||||
except (asyncio.TimeoutError, psycopg2.Error) as err:
|
except (asyncio.TimeoutError, psycopg2.OperationalError) as err:
|
||||||
message = str(err)
|
|
||||||
if isinstance(err, psycopg2.OperationalError) and _attempt == 1:
|
if isinstance(err, psycopg2.OperationalError) and _attempt == 1:
|
||||||
LOGGER.critical('Disconnected from Postgres: %s', err)
|
LOGGER.critical('Disconnected from Postgres: %s', err)
|
||||||
retry = True
|
|
||||||
if not self._postgres_reconnect.locked():
|
if not self._postgres_reconnect.locked():
|
||||||
async with self._postgres_reconnect:
|
async with self._postgres_reconnect:
|
||||||
retry = await self._postgres_connect()
|
if await self._postgres_connect():
|
||||||
if retry:
|
await self._postgres_connected.wait()
|
||||||
await self._postgres_connected.wait()
|
async with self.postgres_connector(
|
||||||
async with self.postgres_connector(
|
on_error, on_duration, timeout,
|
||||||
on_error, on_duration, timeout,
|
_attempt + 1) as connector:
|
||||||
_attempt + 1) as connector:
|
yield connector
|
||||||
yield connector
|
return
|
||||||
return
|
exc = on_error('postgres_connector', ConnectionException(str(err)))
|
||||||
message = 'disconnected'
|
|
||||||
exc = on_error('postgres_connector', ConnectionException(message))
|
|
||||||
if exc:
|
if exc:
|
||||||
raise exc
|
raise exc
|
||||||
else: # postgres_status.on_error does not return an exception
|
else: # postgres_status.on_error does not return an exception
|
||||||
yield None
|
yield None
|
||||||
|
|
||||||
async def postgres_status(self) -> dict:
|
async def postgres_status(self) -> dict:
|
||||||
|
|
59
tests.py
59
tests.py
|
@ -216,6 +216,51 @@ class TransactionRequestHandler(RequestHandler):
|
||||||
'user': self.cast_data(user.row)})
|
'user': self.cast_data(user.row)})
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutErrorRequestHandler(RequestHandler):
|
||||||
|
|
||||||
|
GET_SQL = 'SELECT 1;'
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
|
await self.postgres_execute(self.GET_SQL)
|
||||||
|
raise web.HTTPError(500, 'This should have failed')
|
||||||
|
|
||||||
|
def _on_postgres_error(self,
|
||||||
|
metric_name: str,
|
||||||
|
exc: Exception) -> typing.Optional[Exception]:
|
||||||
|
"""Override for different error handling behaviors
|
||||||
|
|
||||||
|
Return an exception if you would like for it to be raised, or swallow
|
||||||
|
it here.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(exc, asyncio.TimeoutError):
|
||||||
|
raise web.HTTPError(418)
|
||||||
|
return exc
|
||||||
|
|
||||||
|
|
||||||
|
class UnhandledExceptionRequestHandler(RequestHandler):
|
||||||
|
|
||||||
|
GET_SQL = 'SELECT 100 / 0;'
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
|
try:
|
||||||
|
await self.postgres_execute(self.GET_SQL)
|
||||||
|
except psycopg2.DataError:
|
||||||
|
raise web.HTTPError(422)
|
||||||
|
raise web.HTTPError(500, 'This should have failed')
|
||||||
|
|
||||||
|
def _on_postgres_error(self,
|
||||||
|
metric_name: str,
|
||||||
|
exc: Exception) -> typing.Optional[Exception]:
|
||||||
|
"""Override for different error handling behaviors
|
||||||
|
|
||||||
|
Return an exception if you would like for it to be raised, or swallow
|
||||||
|
it here.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return exc
|
||||||
|
|
||||||
|
|
||||||
class Application(sprockets_postgres.ApplicationMixin,
|
class Application(sprockets_postgres.ApplicationMixin,
|
||||||
app.Application):
|
app.Application):
|
||||||
|
|
||||||
|
@ -257,8 +302,10 @@ class TestCase(testing.SprocketsHttpTestCase):
|
||||||
web.url('/no-error', NoErrorRequestHandler),
|
web.url('/no-error', NoErrorRequestHandler),
|
||||||
web.url('/no-row', NoRowRequestHandler),
|
web.url('/no-row', NoRowRequestHandler),
|
||||||
web.url('/status', StatusRequestHandler),
|
web.url('/status', StatusRequestHandler),
|
||||||
|
web.url('/timeout-error', TimeoutErrorRequestHandler),
|
||||||
web.url('/transaction', TransactionRequestHandler),
|
web.url('/transaction', TransactionRequestHandler),
|
||||||
web.url('/transaction/(?P<test_id>.*)', TransactionRequestHandler)
|
web.url('/transaction/(?P<test_id>.*)', TransactionRequestHandler),
|
||||||
|
web.url('/unhandled-exception', UnhandledExceptionRequestHandler)
|
||||||
])
|
])
|
||||||
return self.app
|
return self.app
|
||||||
|
|
||||||
|
@ -451,6 +498,16 @@ class RequestHandlerMixinTestCase(TestCase):
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(RuntimeError):
|
||||||
await asyncio.gather(invoke_cursor(), invoke_cursor())
|
await asyncio.gather(invoke_cursor(), invoke_cursor())
|
||||||
|
|
||||||
|
@mock.patch('aiopg.cursor.Cursor.execute')
|
||||||
|
def test_timeout_error_when_overriding_on_postgres_error(self, execute):
|
||||||
|
execute.side_effect = asyncio.TimeoutError
|
||||||
|
response = self.fetch('/timeout-error')
|
||||||
|
self.assertEqual(response.code, 418)
|
||||||
|
|
||||||
|
def test_unhandled_exception_in_on_postgres_error(self):
|
||||||
|
response = self.fetch('/unhandled-exception')
|
||||||
|
self.assertEqual(response.code, 422)
|
||||||
|
|
||||||
|
|
||||||
class TransactionTestCase(TestCase):
|
class TransactionTestCase(TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue