mosh442
mosh442

Reputation: 738

Flask, processing requests 1 by 1

I have a flask application which listens for some job to do. The process is quite long (let us say 1 minute) and I would like not allow to process two requests at the same time.

I will be great if once I receive a request, I could close the port flask is listening to and open again when finish. Alternatively I could setup a semaphore but I am not sure about how flask is running concurrently.

Any advice?

from flask import Flask, request
app = Flask(__name__)

@app.route("/",methods=['GET'])
def say_hi():
    return "get not allowed"

@app.route("/",methods=['POST'])
def main_process():
    # heavy process here to run alone
    return "Done"

if __name__ == "__main__":
    app.run(debug=True,host='0.0.0.0')

Upvotes: 18

Views: 18213

Answers (2)

omri_saadon
omri_saadon

Reputation: 10621

You could use a semaphore for this:

import threading
import time
sem = threading.Semaphore()

@app.route("/",methods=['POST'])
def main_process():
    sem.acquire()
    # heavy process here to run alone
    sem.release()
    return "Done"

The semaphore usage is to control the access to a common resource.

You can see more info about semaphore in here

This SO question can help you as well here

EDIT:

As Georg Schölly wrote in comment, The above mentioned solution is problematic in a situation of multiple services.

Although, you can use the wsgi in order to accomplish your goal.

@app.route("/",methods=['POST'])
def main_process():
    uwsgi.lock()
    # Critical section
    # heavy process here to run alone
    uwsgi.unlock()
    return "Done"

uWSGI supports a configurable number of locks you can use to synchronize worker processes

For more info, read here

Upvotes: 15

illright
illright

Reputation: 4043

You could try adding a threading.Lock to indicate that some work is already in progress:

import threading
from contextlib import ExitStack

busy = threading.Lock()
@app.route("/",methods=['POST'])
def main_process():
    if not busy.acquire(timeout = 1):
        return 'The application is busy, refresh the page in a few minutes'

    # ensure busy.release() is called even if an exception is thrown
    with ExitStack() as stack:
        stack.callback(busy.release)
        # heavy process here to run alone

    return "Done"

But Flask by default allows only one request to be processed at a time (more info here), so if you're fine with the fact that during the processing of a single request, all the other users' pages won't load until the process is finished (maybe even get a request timeout error), you don't have to change anything.
If you want to make other users get a message, like in the above code, increase the amount of workers to 2, so that when one worker processes the request, the other one holds off the others.

Upvotes: -2

Related Questions