Handling Database disconnects in SQLAlchemy
Have you heard about connection pool?
A connection pool is a standard technique used to maintain long running connections in memory for efficient re-use, as well as to provide management for the total number of connections an application might use simultaneously.Particularly for server-side web applications, a connection pool is the standard way to maintain a “pool” of active database connections in memory which are reused across requests.
Connection pool is a bed rock for handling database connection. And if the database connection is full it can be messy at times.
A common use case is allow the connection pool to gracefully recover when the database server has been restarted, and all previously established connections are no longer functional. There are two approaches to this.
  1. 1.
    Disconnet Handling - Pessimistic
  2. 2.
    Disconnect Handling - Optimistic
  1. 1.
    Pessimistic Disconnect Handling
Let's look at how we can implement pessimistic disconnection in flask-sqlalchemy package. In config, all you need to set is two configurations. In flask setting configuration you will need to set just the following option:
1
SQLALCHEMY_ENGINE_OPTIONS = {
2
"pool_pre_ping": True,
3
"pool_recycle": 300,
4
}
Copied!
The pessimistic approach refers to emitting a test statement on the SQL connection at the start of each connection pool checkout, to test that the database connection is still viable. Typically, this is a simple statement like “SELECT 1”, but may also make use of some DBAPI-specific method to test the connection for liveness. The approach adds a small bit of overhead to the connection checkout process, however is otherwise the most simple and reliable approach to completely eliminating database errors due to stale pooled connections. The “pre ping” feature will normally emit SQL equivalent to “SELECT 1” each time a connection is checked out from the pool; if an error is raised that is detected as a “disconnect” situation, the connection will be immediately recycled, and all other pooled connections older than the current time are invalidated, so that the next time they are checked out, they will also be recycled before use.
If the database is still not available when “pre ping” runs, then the initial connect will fail and the error for failure to connect will be propagated normally. In the uncommon situation that the database is available for connections, but is not able to respond to a “ping”, the “pre_ping” will try up to three times before giving up, propagating the database error last received.
2. Optimistic DB disconnection
When pessimistic handling is not employed, as well as when the database is shutdown and/or restarted in the middle of a connection’s period of use within a transaction, the other approach to dealing with stale / closed connections is to let SQLAlchemy handle disconnects as they occur, at which point all connections in the pool are invalidated, meaning they are assumed to be stale and will be refreshed upon next checkout. This behavior assumes the Pool is used in conjunction with a Engine. The Engine has logic which can detect disconnection events and refresh the pool automatically. When the Connection attempts to use a DBAPI connection, and an exception is raised that corresponds to a “disconnect” event, the connection is invalidated. The Connection then calls the Pool.recreate() method, effectively invalidating all connections not currently checked out so that they are replaced with new ones upon next checkout. This flow is illustrated by the code example below:
1
from sqlalchemy import create_engine, exc
2
e = create_engine(...)
3
c = e.connect()
4
5
try:
6
# suppose the database has been restarted.
7
c.execute(text("SELECT * FROM table"))
8
c.close()
9
except exc.DBAPIError, e:
10
# an exception is raised, Connection is invalidated.
11
if e.connection_invalidated:
12
print("Connection was invalidated!")
13
14
# after the invalidate event, a new connection
15
# starts with a new Pool
16
c = e.connect()
17
c.execute(text("SELECT * FROM table"))
Copied!
Last modified 2mo ago
Copy link