4 Things I Learned Writing a Flask Application
There are multiple reasons why you should take care of your Flask application
In the past two years, I’ve gotten to know the ins and outs of Flask. Flask is a very flexible Python backend framework that has lots of libraries that can be plugged into your code base without much hassle.
When writing an application, you’ll run into issues that you’ll have to try to understand and find a solution to. The four items I’ll discuss below are things on which I have lost quite some time. I wish I had known about them when I started writing my application two years ago.
I still have lots to learn, so if you’d like to add something to the list, let me know!
Flask SQL Alchemy/RQ
The first thing I’d like to discuss is the usage of flask_sqlalchemy
. It’s great! Together with flask_migrate
, it allowed me to quickly perform database changes in a production environment without having to worry about another developer doing something wrong when migrating manually.
However, an issue with flask_sqlalchemy
is that it has to bind to a Flask application lifecycle. This means that you can only create a database call in the context of an HTTP request. You can solve this by manually creating a context like this:
with app.app_context():
db.create_all()
This is great, but it means you need an application to be initialized. If you have a bigger application in which you want to perform background tasks with RQ/Celery, this means that you’ll have to initialize an app for each worker to use flask_sqlalchemy
. Please don’t do this.
My personal solution was to use the scoped_session
from the sqlalchemy
library throughout my application when it was a worker being active.
This meant that I had to write a wrapper for flask_sqlalchemy
’s SQLAlchemy
object and use it in the worker's context. This saved me huge amounts of time in a worker’s startup!
Looking back, I would advise everyone to use sqlalchemy
directly if you’re writing more than a simple CRUD application. The lifecycle management is great, but it’s not worth all the trouble down the line when you need customization.
SQL Alchemy — Pre_ping
This one has personally caused me a lot of headaches. Our application was running on DigitalOcean, and all of a sudden, it would stop working. I found this very strange because only one other person was using it in the beginning.
When looking at the logs, we had OperationalError: (OperationalError) (2006, 'MySQL server has gone away')
without much more information. Great.
After searching for a lot of time and spending multiple deploys on fixing the issue, I learned that a connection is dropped if an application hasn’t been used in more than eight hours.
Basically, there are two solutions to prevent this from happening. The first one is emitting a test statement on the SQL connection, which is called a pool_pre_ping
. Typically, this is done automatically by SQLAlchemy
by sending a statement like SELECT 1
to the SQL server before doing your query. This adds a small overhead, but according to the documentation, it is:
“The most simple and reliable approach to completely eliminating database errors due to stale pooled connections.” — SQLAlchemy documentation
Another more “optimistic” approach you can use is to work with the pool_recycle
times. By default, this is set to -1
, which means there is no recycle happening. By recycling our pool, connections that have passed a certain age will be closed automatically. This will prevent us from using a stale connection that causes an exception to be thrown and our users to be disappointed.
RQ /SQL Alchemy — Closing DB Connections
Remember when I said not to use flask_sqlalchemy
in background workers? Well, if you still want to use the library, you have to be aware of the fact that it hooks into the lifecycle of your application.
This means: Request made → create SQL session→ Do some work → close SQL session → Send response.
What happens if you’re using background tasks? No request is coming in! This means you’ll manually have to push an application context to your worker.
This brings me to the third thing to pay attention to: In background tasks, always remove your db.session
. This may not seem obvious at first as you see that your application works perfectly without it in the context of requests.
However, in background tasks, flask_sqlalchemy
will not automatically remove your session! So it becomes your responsibility at the end of every background task to remove the session. Otherwise, you’ll end up with all sessions being used and exceptions will start flooding your logs.
So remember: db.session.remove()
is your friend in the context of background jobs.
Using a Proxy (NGinx/Apache)
When you deploy your application, you’ll probably use something like Nginx or Apache as a load balancer/proxy.
The problem is that WSGI may think that the request comes from the Nginx server rather than from a real client. Proxies like Nginx have a lot of headers that allow you to track the actual user from which the request is coming.
That’s why, when deploying your application with a proxy, you should use some custom middleware like Werkzeug’s ProxyFix to allow the app to work properly upon the request.
Remember that in production, you should never use something like flask run
! Always use a WSGI-compliant container for your application like Gunicorn, Unicorn, uWSGI, GEvent, etc.
Conclusion
In this article, I shared some of the troubles I’ve had with the Flask framework in the past two years. Overall, it has been a positive experience that I’m glad to have chosen.
I hope this has helped some of you who wish to get their feet wet in the Flask ecosystem or wanted to take their knowledge a little further. Good luck!