pythonsqlalchemyflask-sqlalchemyuwsgi

SQLalchemy can't communicate to MySQL server upon app start


I'm have troubles with SQLalchemy in Flask app first minute after the app start (or restart). It looks like logger exceptions of a sort:

sqlalchemy.exc.OperationalError: (MySQLdb._exceptions.OperationalError) (2006, 'MySQL server has gone away') <---- most often one

sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.

sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'users.id'"

sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'payments.id'"

sqlalchemy.exc.OperationalError: (MySQLdb._exceptions.OperationalError) (2013, 'Lost connection to MySQL server during query')

Then everything gets back to normal. It's not a critical issue but annoying.

I tried to run warming up queries upon application start:

    with app.app_context():
        for _ in range(5):
            try:
                db.session.execute(statement)
                logger.info("DB connection is successfully established")
                return
            except Exception as e:
                logger.warning(e)
                time.sleep(1)
    raise Exception("Couldn't establish a DB connection")

It passes through just fine but then I see same issues.

It doesn't happen in development environment, only in production where Flask app runs on uwsgi server. Is there a way to fix it?

Update: connection URI looks like this: "mysql+mysqldb://user:password@localhost/mydb?unix_socket=/var/run/mysqld/mysqld.sock"


Solution

  • Seems How to correctly setup Flask + uWSGI + SQLAlchemy to avoid database connection issues worked for them. This is a copy paste of the answer just to avoid "link only answers". I would still recommend anyone arriving at this question to refer the original answer linked above.

    The SQLAlchemy manual provides two examples how to approach this: Using Connection Pools with Multiprocessing. The first approach involving Engine.dispose() can be approached using uwsgidecorators.postfork as suggested by Hett - simple example that should work if there's only the default binding involved:

        db = SQLAlchemy()
    
        def create_app():
            app = Flask(__name__)
            app.config["SQLALCHEMY_DATABASE_URI"] = "postgres:///test"
            db.init_app(app)
            
            def _dispose_db_pool():
                with app.app_context():
                    db.engine.dispose()
    
            try:
                from uwsgidecorators import postfork
                postfork(_dispose_db_pool)
            except ImportError:
                # Implement fallback when running outside of uwsgi...
                raise
    
            return app
    

    Meta: I am posting this as an answer as per OP's comment