mirror of
https://github.com/sprockets/sprockets-postgres.git
synced 2024-11-14 11:19:27 +00:00
Include a StatusRequestHandler to make things easy
This commit is contained in:
parent
507704a6e7
commit
9a49d769df
3 changed files with 39 additions and 17 deletions
|
@ -9,3 +9,14 @@ functionality for interacting with PostgreSQL.
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
:private-members:
|
:private-members:
|
||||||
:member-order: bysource
|
:member-order: bysource
|
||||||
|
|
||||||
|
The :class:`~sprockets_postgres.StatusRequestHandler` is a Tornado
|
||||||
|
:class:`tornado.web.RequestHandler` that can be used for application health
|
||||||
|
monitoring. If the Postgres connection is unavailable, it will report the
|
||||||
|
API as unavailable and return a 503 status code.
|
||||||
|
|
||||||
|
.. autoclass:: sprockets_postgres.StatusRequestHandler
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:private-members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
|
@ -717,3 +717,21 @@ class RequestHandlerMixin:
|
||||||
else:
|
else:
|
||||||
LOGGER.debug('Postgres query %s duration: %s',
|
LOGGER.debug('Postgres query %s duration: %s',
|
||||||
metric_name, duration)
|
metric_name, duration)
|
||||||
|
|
||||||
|
|
||||||
|
class StatusRequestHandler(web.RequestHandler):
|
||||||
|
"""A RequestHandler that can be used to expose API health or status"""
|
||||||
|
|
||||||
|
async def get(self, *_args, **_kwarg):
|
||||||
|
postgres = await self.application.postgres_status()
|
||||||
|
if not postgres['available']:
|
||||||
|
self.set_status(503)
|
||||||
|
self.write({
|
||||||
|
'application': self.settings.get('service', 'unknown'),
|
||||||
|
'environment': self.settings.get('environment', 'unknown'),
|
||||||
|
'postgres': {
|
||||||
|
'pool_free': postgres['pool_free'],
|
||||||
|
'pool_size': postgres['pool_size']
|
||||||
|
},
|
||||||
|
'status': 'ok' if postgres['available'] else 'unavailable',
|
||||||
|
'version': self.settings.get('version', 'unknown')})
|
||||||
|
|
27
tests.py
27
tests.py
|
@ -175,15 +175,6 @@ class NoRowRequestHandler(RequestHandler):
|
||||||
'rows': self.cast_data(result.rows)})
|
'rows': self.cast_data(result.rows)})
|
||||||
|
|
||||||
|
|
||||||
class StatusRequestHandler(RequestHandler):
|
|
||||||
|
|
||||||
async def get(self):
|
|
||||||
status = await self.application.postgres_status()
|
|
||||||
if not status['available']:
|
|
||||||
self.set_status(503, 'Database Unavailable')
|
|
||||||
await self.finish(status)
|
|
||||||
|
|
||||||
|
|
||||||
class TransactionRequestHandler(RequestHandler):
|
class TransactionRequestHandler(RequestHandler):
|
||||||
|
|
||||||
GET_SQL = """\
|
GET_SQL = """\
|
||||||
|
@ -315,7 +306,7 @@ 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('/row-count-no-rows', RowCountNoRowsRequestHandler),
|
web.url('/row-count-no-rows', RowCountNoRowsRequestHandler),
|
||||||
web.url('/status', StatusRequestHandler),
|
web.url('/status', sprockets_postgres.StatusRequestHandler),
|
||||||
web.url('/timeout-error', TimeoutErrorRequestHandler),
|
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),
|
||||||
|
@ -329,29 +320,31 @@ class RequestHandlerMixinTestCase(TestCase):
|
||||||
def test_postgres_status(self):
|
def test_postgres_status(self):
|
||||||
response = self.fetch('/status')
|
response = self.fetch('/status')
|
||||||
data = json.loads(response.body)
|
data = json.loads(response.body)
|
||||||
self.assertTrue(data['available'])
|
self.assertEqual(data['status'], 'ok')
|
||||||
self.assertGreaterEqual(data['pool_size'], 1)
|
self.assertGreaterEqual(data['postgres']['pool_size'], 1)
|
||||||
self.assertGreaterEqual(data['pool_free'], 1)
|
self.assertGreaterEqual(data['postgres']['pool_free'], 1)
|
||||||
|
|
||||||
@mock.patch('aiopg.pool.Pool.acquire')
|
@mock.patch('aiopg.pool.Pool.acquire')
|
||||||
def test_postgres_status_connect_error(self, acquire):
|
def test_postgres_status_connect_error(self, acquire):
|
||||||
acquire.side_effect = asyncio.TimeoutError()
|
acquire.side_effect = asyncio.TimeoutError()
|
||||||
response = self.fetch('/status')
|
response = self.fetch('/status')
|
||||||
self.assertEqual(response.code, 503)
|
self.assertEqual(response.code, 503)
|
||||||
self.assertFalse(json.loads(response.body)['available'])
|
data = json.loads(response.body)
|
||||||
|
self.assertEqual(data['status'], 'unavailable')
|
||||||
|
|
||||||
def test_postgres_status_not_connected(self):
|
def test_postgres_status_not_connected(self):
|
||||||
self.app._postgres_connected.clear()
|
self.app._postgres_connected.clear()
|
||||||
response = self.fetch('/status')
|
response = self.fetch('/status')
|
||||||
self.assertEqual(response.code, 503)
|
self.assertEqual(response.code, 503)
|
||||||
self.assertFalse(json.loads(response.body)['available'])
|
data = json.loads(response.body)
|
||||||
|
self.assertEqual(data['status'], 'unavailable')
|
||||||
|
|
||||||
@mock.patch('aiopg.cursor.Cursor.execute')
|
@mock.patch('aiopg.cursor.Cursor.execute')
|
||||||
def test_postgres_status_error(self, execute):
|
def test_postgres_status_error(self, execute):
|
||||||
execute.side_effect = asyncio.TimeoutError()
|
execute.side_effect = asyncio.TimeoutError()
|
||||||
response = self.fetch('/status')
|
response = self.fetch('/status')
|
||||||
self.assertEqual(response.code, 503)
|
data = json.loads(response.body)
|
||||||
self.assertFalse(json.loads(response.body)['available'])
|
self.assertEqual(data['status'], 'unavailable')
|
||||||
|
|
||||||
def test_postgres_callproc(self):
|
def test_postgres_callproc(self):
|
||||||
response = self.fetch('/callproc')
|
response = self.fetch('/callproc')
|
||||||
|
|
Loading…
Reference in a new issue