Using Edgy is designed to be straightforward, but understanding connection management is crucial for optimal performance and stability.
Edgy is built on SQLAlchemy Core, but it's an asynchronous implementation. This raises questions about its integration with popular frameworks like Esmerald, Starlette, or FastAPI.
Edgy is framework-agnostic, meaning it can be seamlessly integrated into any framework that supports lifecycle events.
Lifecycle events are common in frameworks built on Starlette, such as Esmerald and FastAPI. Other frameworks may offer similar functionality through different mechanisms.
The most common lifecycle events include:
- on_startup
- on_shutdown
- lifespan
This document focuses on lifespan
, which is widely used.
Integrating database connections is as simple as incorporating them into your framework's lifecycle events.
For illustrative purposes, we'll use Esmerald. However, the principles apply to any framework.
Using ASGI integration:
{!> ../docs_src/connections/asgi.py !}
Manual integration (applicable to all frameworks):
{!> ../docs_src/connections/simple.py !}
Using an asynchronous context manager:
{!> ../docs_src/connections/asynccontextmanager.py !}
Once the connection is integrated into your application's lifecycle, you can use the ORM throughout your application. Failing to do so will result in performance warnings, as the databasez backend will be reinitialized for each operation.
You can also define additional database connections in the registry and switch between them.
Django doesn't natively support the lifespan protocol. Therefore, we provide a keyword parameter for manual handling.
{!> ../docs_src/connections/django.py !}
The __aenter__
and __aexit__
methods can be called as connect
and disconnect
. However, using context managers is recommended for simpler error handling.
{!> ../docs_src/connections/manual.py !}
This approach is suitable for integration via on_startup
and on_shutdown
.
{!> ../docs_src/connections/manual_esmerald.py !}
This warning appears when an unconnected Database
object is used.
Despite the warning being non-fatal, you should establish proper connections as demonstrated above. Synchronous environments require additional care.
!!! Note
Ensure that Database
objects passed via using
are connected. They are not guaranteed to be connected outside of extra
.
When the framework is synchronous and no asynchronous loop is active, we can use run_sync
. It's necessary to create an asynchronous environment using the with_async_env
method of the registry. Otherwise, you'll encounter performance issues and DatabaseNotConnectedWarning
warnings. run_sync
calls must occur within the scope of with_async_env
. with_async_env
is re-entrant and accepts an optional loop parameter.
{!> ../docs_src/connections/contextmanager.py !}
To maintain the loop for performance reasons, you can wrap the server worker loop or, for single-threaded servers, the server loop that runs the application. Alternatively, you can keep the asyncio event loop alive, which is easier for synchronous-first frameworks like Flask. Here's an example that's multi-threading safe.
{!> ../docs_src/connections/contextmanager_with_loop.py !}
That was complicated, right? Let's unroll it in a simpler example with explicit loop cleanup.
{!> ../docs_src/connections/contextmanager_with_loop_and_cleanup.py !}
Note: with_async_env
internally calls __aenter__
and __aexit__
. Therefore, the database is connected during the with
scope of with_async_env
. This means you can use run_sync
and run commands in another loop (e.g., via asyncio.run
). Everything works without raising DatabaseNotConnectedWarning
. This is used, for example, in edgy shell
.
run_sync
requires further explanation. It integrates with the asynchronous environment created by with_async_env
and prefers checking for an active running loop (unless an explicit loop is provided). If an active loop is found, a subloop is created, which is only terminated when the found loop (or explicit loop) is garbage collected. If an idling loop is found, it's reused instead of creating a subloop.
What is a subloop?
A subloop is an event loop running in a separate thread. This allows multiple event loops to run concurrently. They are removed when the parent event loop is garbage collected.
However, given that event loops can be sticky, we additionally check if the old loop has stopped.
Edgy supports querying other schemas. Refer to the tenancy section for details.
The Edgy Registry accepts an extra
parameter for defining named additional Database
objects or strings. Including them here ensures they're connected and disconnected appropriately.
You can switch between them using using.
See Migrations for more informations.
Check the tips and tricks and learn how to make your connections even cleaner.