Change gunicorn worker timeout inside request - python

I have a slow request, and I want to change timeout for the worker during handling of that request and only for that request.
Basically, I have a flask application:
class Slow(Resource):
def post(self):
if slow_condition():
gunicorn.how.to.extend.processing.time.here()
do_something_slow()
api = Api(application)
api.add_resource(Slow, "/slow")
and I want to extend processing time if slow_condition returned True. How can I change timeout for the single request?

No way.
Flask is just web framework. Framework doesn't know anything about production server and settings, workers etc. By the way to change sever configuration you need to reload all workers(restart gunicorn, uWSGI, waitress etc). So you can only increase timeout parameter.

Related

how to hold new request when old request is processing in flask?

I created a python app in flask. Here the Skelton of the code
app = Flask(__name__)
#app.route('/', methods=['GET'])
def authentication():
'''athentication process'''
return 'authenticated'
so when user call the app it will authenticate. but if two user call that at same time or while processing one authentication i want to hold the new request until the old one finished then I want to start the new request. I've tried with semaphore but not working. Here is what I've tried
#app.route('/', methods=['GET'])
def authentication():
sem.acquire()
'''athentication process'''
sem.release()
return 'authenticated'
and I have deployed this in Heroku. Any idea how I can achieve this?
PS: If this can't be done at least i want to response the new request that another request is in process and try again after some time
Short answer: Dont worry about it.
This is the job of a web server. When you host the application in any Server like Apache , Nginx etc the server creates multiple processes of your flask app.When requst comes the, server program forwards it to any of the free processes, if no process is free server will queue the request until one process becomes free.
This is high level overwiew of how HTTP servers work.

How to get flask request context in celery task?

I have a flask server running within a gunicorn.
In my flask application I want to handle large upload files (>20GB), so I plan on letting a celery task do the handling of the large file.
The problem is that retrieving the file from request.files already takes quite long, in the meantime gunicorn terminates the worker handling that request. I could increase the timeout time, but the maximum file size is currently unknown, so I don't know how much time I would need.
My plan was to make the request context available to the celery task, as it was described here: http://xion.io/post/code/celery-include-flask-request-context.html, but I cannot make it work
Q1 Is the signature right?
I set the signature with
celery.signature(handle_large_file, args={}, kwargs={})
and nothing is complaining. I get the arguments I pass from the flask request handler to the celery task, but that's it. Should I somehow get a handle to the context here?
Q2 how to use the context?
I would have thought if the flask request context was available I could just use request.files in my code, but then I get the warning that I am out of context.
Using celery 4.4.0
Code:
# in celery.py:
from flask import request
from celery import Celery
celery = Celery('celery_worker',
backend=Config.CELERY_RESULT_BACKEND,
broker=Config.CELERY_BROKER_URL)
#celery.task(bind=True)
def handle_large_file(task_object, data):
# do something with the large file...
# what I'd like to do:
files = request.files['upfile']
...
celery.signature(handle_large_file, args={}, kwargs={})
# in main.py
def create_app():
app = Flask(__name__.split('.')[0])
...
celery_worker.conf.update(app.config)
# copy from the blog
class RequestContextTask(Task):...
celery_worker.Task = RequestContextTask
# in Controller.py
#FILE.route("", methods=['POST'])
def upload():
data = dict()
...
handle_large_file.delay(data)
What am I missing?

gunicorn and flask-jwt-extended not getting current user

I've been building an app that uses flask, flask_jwt_extended and the decorator #jwt_required around protected functions that need an access token to access. From these endpoints, I can use flask_jwt_extended's get_current_user function to fetch the currrent user.
This has been working fine while in development, but now I'm planning to serve a production application using gunicorn. However, when I run the gunicorn server, it seems like the get_current_user function is always returning none. For example, the following code works with python3 -m flask run, but not with gunicorn run:app -b localhost:5000. What could be the problem here?
#jwt_required
def get_user_by_id(user_id: str) -> Dict[str, Any]:
# returns user when using flask run, but not with gunicorn
curr_user = get_current_user()
Class-worker
Do you use multiple workers and sync class ?
Maybe try to look at gunicorn worker-class
If you try to use the sync worker type and set the threads setting to more than 1, the gthread worker type will be used instead.
In your python configuration file or you can change worker-class directly in the command line.
worker_class = 'gthread'
Check your JWT_SECRET_KEY
JWT_SECRET_KEY need to have the same value all over each workers

Python: Making a Flask Rest API Asynchronous and Deploying it

I have a python server that is currently keeping track of the location of all the buses in my university and generating predictions of arrivals to specific locations.
Now, I wanted to attach a lightweight REST API to this server but I have been running intro problems.
I tried using flask with the following code:
from flask import Flask, jsonify
from PredictionWrapper import *
import threading
class RequestHandler():
def __init__(self,predictionWrapper):
self.app = Flask(__name__)
self.predictor = predictionWrapper
self.app.debug = False
self.app.add_url_rule('/<route>/<int:busStop>','getSinglePrediction',self.getSinglePrediction)
t = threading.Thread(target=self.app.run, kwargs={'host':'0.0.0.0', 'port':80, 'threaded':True})
t.start()
def getSinglePrediction(self, route, busStop):
# TODO Get the actual prediction with given parameters
prediction = self.predictor.getPredictionForStop(route, busStop)
return jsonify({'busStop': busStop, 'prediction': prediction})
def getStopPrediction(self, busStop):
# TODO Get the actual prediction with given parameters
return jsonify({'busStop': busStop, 'prediction': 2})
def run(self):
self.app.run(host='0.0.0.0', port=80, threaded=True)
The problem is that I have been encountering the error below after about half a day of running the server. Note that no requests were made to the server around the time it failed with the following error:
ERROR:werkzeug: - - [01/May/2016 09:55:55] code 400, message Bad request syntax ('\x02\xfd\xb1\xc5!')
After investigating I believe I need to deploy to a WSGI production server. But I have no clue what it means in this specific approach given that 1)the flask server is being threaded in order to run the rest of the prediction application, and 2)I am using classes which none of the documentation uses.
Any help on how to setup the wsgi file with apache, gunicorn, or the technology of your choice would be appreciated. Also, any comments on a better approach on making a non-blocking REST API would be helpful.
Let me know if you need any further clarification!
Not sure if this can actually solve your problem but you can use the coroutine based web server gevent. They have a WSGI server that you can use if that's what you meant by saying that you need to deploy a WSGI production server.
If you want to implement the server to your flask application just do the following:
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
Gevent in general is a very powerful tool and by issuing context switches as necessary it can handle multiple clients very easily. Also, gevent fully supports flask.
First thing to do would be to put exception handling to deal with bad JSON request data (which maybe is what's happening) something like
def getSinglePrediction(self, route, busStop):
try:
prediction = self.predictor.getPredictionForStop(route, busStop)
return jsonify({'busStop': busStop, 'prediction': prediction})
except:
return jsonify({'busStop': 'error', 'prediction': 'error'})

Streaming server issue with gunicorn and flask and Nginx

I am using gunicorn and flask for a web service. I am trying to get my head around running a streaming route (not sure if that is the correct terminology).
my route looks like this:
#app.route('/delay')
def delay():
from time import sleep
def delay_inner():
for i in range(10):
sleep(5)
yield json.dumps({'delay': i})
return Response(delay_inner(), mimetype="text/event-stream")
I expect that the server would yield the output each time that delay_inner does a yield. But, what I am getting is all the json responses at once, and only when the delay_inner finishes execution.
What am I missing here?
--EDIT--
I have fixed the issue for Flask and Gunicorn, I am able to run it as expected by using the flask server, and by going to the Gunicorn port. It streams the data as expected. However, and I should have mentioned this in the original post, I am also running behind nginx. And that is not set up correctly to stream. Can anyone help with that?
You need to turn off the nginx proxy buffering.
location /delay {
proxy_pass http://127.0.0.1:8080;
proxy_buffering off;
}
and reload the config
nginx -s reload

Categories