citrusvanilla
citrusvanilla

Reputation: 59

Missing Flask application context in enqueued background worker thread deployed from Blueprint

I have a Flask application that is using the factory function pattern (from the intro tutorial) and am attempting to offload a long running job to a background worker with a Redis queue. I am invoking the background work from a Blueprint, and am unable to pass the application context along with the invocation. The intention is to use the application context and the SQLite configuration to perform writes to it from the background thread. What am I missing here? I think this may be more of a "you just don't know enough about how Flask works" issue and if that is the case, please let me know what I'm doing wrong! Thanks.

ERROR

RuntimeError: Working outside of application context.

dy.py

import sqlite3

import click
from flask import current_app, g
from flask.cli import with_appcontext


def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES
        )
        g.db.row_factory = sqlite3.Row
    
    return g.db

Blueprint module:

from flask import (
    Blueprint, flash, g, redirect, render_template, request, url_for, Response, current_app
)
import sqlite3

from app.db import get_db

from rq import Queue
from redis import Redis

bp = Blueprint('perform_work', __name__)
q = Queue(connection=Redis())


def some_func():

    db = get_db()
    ...
    

def generate_work():

    result = q.enqueue(some_func)
    ...


@bp.route('/perform_work', methods=['POST'])
def perform_work():
    ...
    generate_work()

worker.py

import os

import redis
from rq import Worker, Queue, Connection

listen = ['default']

redis_url = os.getenv('REDISTOGO_URL', 'redis://localhost:6379')

conn = redis.from_url(redis_url)


if __name__ == '__main__':
    with Connection(conn):
        worker = Worker(list(map(Queue, listen)))
        worker.work()

Upvotes: 0

Views: 674

Answers (1)

citrusvanilla
citrusvanilla

Reputation: 59

alright well im glad i typed this out i guess. the context of the app was never registered with the worker, which should've occurred in worker.py, so yeah the worker had no idea about the application itself. here's the updated worker.py that registers the app's context:

import os

import redis
from rq import Worker, Queue, Connection
from app import create_app

listen = ['default']

redis_url = os.getenv('REDISTOGO_URL', 'redis://localhost:6379')

conn = redis.from_url(redis_url)

app = create_app()
app.app_context().push()

if __name__ == '__main__':
    with Connection(conn):
        worker = Worker(list(map(Queue, listen)))
        worker.work()

Upvotes: 1

Related Questions