From 7739e426a31976d89333319586b4c7e67f6d840a Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Wed, 27 Oct 2021 21:11:08 +0200 Subject: [PATCH 1/4] Add a faq entry based on @vytas7 answer at https://github.com/falconry/falcon/discussions/1981#discussioncomment-1544203 --- docs/user/faq.rst | 78 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/docs/user/faq.rst b/docs/user/faq.rst index 2c60e42a3..073b0ade0 100644 --- a/docs/user/faq.rst +++ b/docs/user/faq.rst @@ -964,7 +964,8 @@ Then, within ``SomeResource``: .. code:: python # Read from the DB - result = self._engine.execute(some_table.select()) + with self._engine.connect() as connection: + result = connection.execute(some_table.select()) for row in result: # TODO: Do something with each row @@ -998,6 +999,81 @@ use cases that may not be supported by your client library, simply encapsulate the client library within a management class that handles all the tricky bits, and pass that around instead. +The library `Falcon SQLAlchemy `_ +can also be used to manage SQLAlchemy connection using the a middleware. + +How do I manage my database connections with ASGI? +-------------------------------------------------- + +This example example is similar to the above one but uses the ASGI lifecycle hooks +to setup a connection pool and to dispose of it at the end of the application. +The example uses the `psycopg https://www.psycopg.org/psycopg3/docs/api/index.html`_ +library to connect to a PostgreSQL database, but a similar pattern may be adapted +to other libraries. + +.. code:: python + import psycopg_pool + + url = 'postgresql://scott:tiger@127.0.0.1:5432/test' + + class AsyncPoolMiddleware: + def __init__(self): + self._pool = None + + async def process_startup(self, scope, event): + self._pool = psycopg_pool.AsyncConnectionPool(url) + await self._pool.wait() # created the pooled connections + + async def process_shutdown(self, scope, event): + if self._pool: + await self._pool.close() + + async def process_request(self, req, resp): + req.context.pool = self._pool + + try: + req.context.conn = await self._pool.getconn() + except Exception: + req.context.conn = None + raise + + async def process_response(self, req, resp, resource, req_succeeded): + if req.context.conn: + await self._pool.putconn(req.context.conn) + +Then, an example resource may use the connection or the pool: + +.. code:: python + + class Numbers: + async def on_get(self, req, resp): + # This endpoint uses the connection created for the request by the Middleware + async with req.context.conn.cursor() as cur: + await cur.execute('SELECT value FROM numbers') + rows = await cur.fetchall() + + resp.media = [row[0] for row in rows] + + async def on_get_with_pool(self, req, resp): + # This endpoint uses the pool to acquire a connection + async with req.context.pool.connection() as conn: + cur = await conn.execute('SELECT value FROM numbers') + rows = await cur.fetchall() + await cur.close() + + resp.media = [row[0] for row in rows] + +The application can then be used as + +.. code:: python + + from falcon.asgi import App + + app = App(middleware=[AsyncPoolMiddleware()]) + num = Numbers() + app.add_route('/conn', num) + app.add_route('/pool', num, suffix='with_pool') + .. _configuration-approaches: What is the recommended approach for app configuration? From 6c798655e02de737c9efc63205b2b11e700d99e6 Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Wed, 27 Oct 2021 21:19:11 +0200 Subject: [PATCH 2/4] fix rst --- docs/user/faq.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/user/faq.rst b/docs/user/faq.rst index 073b0ade0..c5b36db4f 100644 --- a/docs/user/faq.rst +++ b/docs/user/faq.rst @@ -1012,6 +1012,7 @@ library to connect to a PostgreSQL database, but a similar pattern may be adapte to other libraries. .. code:: python + import psycopg_pool url = 'postgresql://scott:tiger@127.0.0.1:5432/test' From f6d07217ea7aad54ebf89eae873d151ec6e6b5fd Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Wed, 27 Oct 2021 21:26:03 +0200 Subject: [PATCH 3/4] fix(docs): adress review feedback --- docs/user/faq.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/user/faq.rst b/docs/user/faq.rst index c5b36db4f..eec1b3460 100644 --- a/docs/user/faq.rst +++ b/docs/user/faq.rst @@ -1000,14 +1000,14 @@ the client library within a management class that handles all the tricky bits, and pass that around instead. The library `Falcon SQLAlchemy `_ -can also be used to manage SQLAlchemy connection using the a middleware. +can also be used to manage SQLAlchemy connection using a middleware. How do I manage my database connections with ASGI? -------------------------------------------------- -This example example is similar to the above one but uses the ASGI lifecycle hooks -to setup a connection pool and to dispose of it at the end of the application. -The example uses the `psycopg https://www.psycopg.org/psycopg3/docs/api/index.html`_ +This example is similar to the above one but uses the ASGI lifecycle hooks +to setup a connection pool and to dispose it at the end of the application. +The example uses the `psycopg `_ library to connect to a PostgreSQL database, but a similar pattern may be adapted to other libraries. From 43046c44ed342c665ab95acc1c6c5ccb359322e3 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Sun, 7 Nov 2021 15:08:56 +0100 Subject: [PATCH 4/4] docs(FAQ): tweak prose --- docs/user/faq.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/user/faq.rst b/docs/user/faq.rst index eec1b3460..5a3c647dd 100644 --- a/docs/user/faq.rst +++ b/docs/user/faq.rst @@ -999,17 +999,19 @@ use cases that may not be supported by your client library, simply encapsulate the client library within a management class that handles all the tricky bits, and pass that around instead. -The library `Falcon SQLAlchemy `_ -can also be used to manage SQLAlchemy connection using a middleware. +If you are interested in the middleware approach, the +`falcon-sqla `__ library can be used to +automatically check out and close SQLAlchemy connections that way (although it +also supports the explicit context manager pattern). How do I manage my database connections with ASGI? -------------------------------------------------- -This example is similar to the above one but uses the ASGI lifecycle hooks -to setup a connection pool and to dispose it at the end of the application. -The example uses the `psycopg `_ -library to connect to a PostgreSQL database, but a similar pattern may be adapted -to other libraries. +This example is similar to the above one, but it uses ASGI lifecycle hooks +to set up a connection pool, and to dispose it at the end of the application. +The example uses `psycopg `_ +to connect to a PostgreSQL database, but a similar pattern may be adapted to +other asynchronous database libraries. .. code:: python