Search code examples
pythonpython-3.xflaskpeeweeapscheduler

Flask and peewee FlaskDB() "connection already opened" with APScheduler


I have a flask application with peewee and a pooled postgresql database.

Everything works with the app setup until I added an APScheduler job. I want the job to run when app starts and every 8 hours after.

config bits for flask-apscheduler and peewee:

class Config(object):
    JOBS =  [
        {
            'id': 'myjob',
            'func': 'app.jobs:do_something_job',
            'trigger': 'interval',
            'hours': 8
        }
        ]
    SCHEDULER_API_ENABLED = True
    DATABASE = 'postgresext+pool://user:password@localhost:5432/dev?max_connections=32&stale_timeout=300'

app.py:

scheduler = APScheduler()
db = FlaskDB()

def create_app(config_object=Config):
    """Application Factory Pattern"""
    app = Flask(__name__.split('.')[0])
    app.config.from_object(config_object)
    db.init_app(app)
    scheduler.init_app(app)
    scheduler.start()
    # RUN the job on application creation:
    scheduler.run_job('myjob') # <--- exception thrown here
    return app

app = create_app(Config)

the line scheduler.run_job('myjob') results in a peewee.OperationalError: Connection already opened. right after the app is launched (if i visit the page shortly after starting the app)

Although, it appears the initial job run still works fine

The do_something_job looks something like:

def do_something_job():
    new = NewModel(seed=new_seed())
    new.save()
    old_list = NewModel.select()
    for old in old_list:
        if old.id != new.id:
            expired = OldModel(seed=old.seed,
                       created_at=old.created_at,
                       expired_at=datetime.datetime.utcnow())
            expired.save()
            old.delete_instance()

I'm just not sure exactly what I'm missing, I'm a bit novice when it comes to peewee/flask still.

Thanks!


Solution

  • I'll wait awhile before accepting my own answer because I'm sure there's a potentially better/proper solution.

    But I basically setup Flask-APScheduler to run the initial job 30 seconds in the future and removed it from my create_app().

    I'm not sure if it's because the database isn't initialized yet at the point the code runs or what exactly and i'm sure this is hackish but it did clear up the exception

    Basically added a second job to config (and explicit timezone)

    class Config(object):
        SCHEDULER_TIMEZONE = 'UTC'
        JOBS =  [
            {
                'id': 'myinitialjob',
                'func': 'app.jobs:do_something_job',
                'trigger': 'date',
                'run_date': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)
            },
            {
                'id': 'myjob',
                'func': 'app.jobs:do_something_job',
                'trigger': 'interval',
                'hours': 8
            }
            ]
        SCHEDULER_API_ENABLED = True
    

    Removed the initial run from the create_app func

    def create_app(config_object=Config):
        """Application Factory Pattern"""
        app = Flask(__name__.split('.')[0])
        app.config.from_object(config_object)
        db.init_app(app)
        scheduler.init_app(app)
        scheduler.start()
        # scheduler.run_job('myjob') # <--- removed this
        return app