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

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.

Related

how concurrency works in flask and is handled by whom?

I have a time-consuming task in route /fibo and a route /home that return hello.
from flask import Flask, request
from fibo import fib_t
from datetime import datetime
app = Flask(__name__)
#app.route('/home')
def hello():
return 'hello '
#app.route('/fibo')
def cal_fibo():
request_rec_t = datetime.now().strftime("%M:%S")
v, duretion = fib_t(37)
return f'''
<div><h1>request recieved by server:</h1>
<p>{request_rec_t}</p>
<p>
<ul>
<li><b>calculated value</b>:{v}</li>
<li><b>that takes</b>: {duretion} seconds</li>
<ul>
</p>
</div>
<div>
<h1>resonse send by server:</h1>
<p>{datetime.now().strftime("%M:%S")}</p>
</div>'''
if __name__ == '__main__':
app.run(debug=True)
the fibo module
import time
def fib(a):
if a == 1 or a==2:
return 1
else:
return fib(a-1) + fib(a-2)
def fib_t(a):
t = time.time()
v = fib(a)
return v, f'{time.time()-t:.2f}'
if I made two requests to /fibo the second one will start running after the first one finishes his work.
but if I send a request to the time-consuming task and another one to /home I will receive the /home route response immediately while still first request isn't resolved.
if this is matters, I make the requests from different tabs of the browser.
for example, if I request /fibo in two different tabs. the page starts to load. the captured time for the first request is 10:10 and 10:20(the execution of Fibonacci number is tasked 10 seconds) during this request to be finished, the second tab still is loading. after that the second request is finished i see that the process started at the 10:20(the time that first request finishes process) and finished in 10:29(the execution of Fibonacci number is tasked 9 seconds).
but if I request /home while the /fibo in the first tab isn't returned, I will get the response.
why this is happening?
my general question is how and by whom the multiple requests are being handled?
how can I log the time requests reached the server?
EDIT:
if I add another route fibo2 with the same logic as cal_fibo view function, now I can run them concurrently(like /fibo and /home that I showed that are running concurrently, /fibo and /fibo2 are also running concurrently.)
EDIT 2:
as shown in the image, the second request was sent after the first request's response was received. why and how did this has happened?
(the related TCP handshake of each request has happened close to each other but how and who managed that the related HTTP get request is sent after the first request's response received?)
actually you have only one process running with only one thread all the time to enable threading and running more than one process this depends on the context (development or production) .
for development purposes
to enable threading you need to run
app.run(host="your.host", port=4321, threaded=True)
also to enable running more than one process e.g. 3 according to documentation
you need to run the following line
app.run(host="your.host", port=4321, processes=3)
for production purposes
when you run the app in production environment it is the responsability of the WSGI gateway (eg. gunicorn) that you configure on the cloud provider like heroku or aws to support the handling of multiple requests.
we use this method in production as it is more robust to crashes and also more efficent.
this was just a summary of the answers in this question

Change gunicorn worker timeout inside request

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.

Flask: how to ignore new requests if last one is not done yet

I am trying to build REST API with only one call.
Sometimes it takes up to 30 seconds for a program to return a response. But if user thinks that service is lagging - he makes a new call and my app returns response with error code 500 (Internal Server Error).
For now it is enough for me to block any new requests if last one is not ready. Is there any simple way to do it?
I know that there is a lot of queueing managers like Celery, but I prefer not to overload my app with any large dependencies/etc.
You could use Flask-Limiter to ignore new requests from that remote address.
pip install Flask-Limiter
Check this quickstart:
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
#app.route("/slow")
#limiter.limit("1 per day")
def slow():
return "24"
#app.route("/fast")
def fast():
return "42"
#app.route("/ping")
#limiter.exempt
def ping():
return "PONG"
As you can see, you could ignore the remote IP address for a certain amount of time meanwhile you finish the process you´re running
DOCS
Check these two links:
Flasf-Limiter Documentation
Flasf-Limiter Quick start

Flask Client-Server Connection Timeout after given time

I have a flask app. I want the client-server connection to terminate if the server does not respond within a stipulated time (say 20 seconds). I read here that the session.permanent = True can be set. I am a bit unclear where this goes in the server side code (if at all this is the way??).
For simplicity I am including the minimal server side code I have. Actually the server is performing a File Read/Write operation and returning a result to the client.
from flask import Flask, session, app
from flask_restful import Api, Resource
from datetime import timedelta
app = Flask(__name__)
api = Api(app)
class GetParams(Resource):
def get(self):
print ("Hello.")
return 'OK'
api.add_resource(GetParams, '/data')
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5002)
Can anyone tell me what should I do here so that the connection between my client and server is terminated if the server does not respond i.e., send data back to the client within 20 seconds?
Long running tasks should be dealt with in a different design because, if you allow your server to keep a request alive for 50 minutes, you can't force user browser to do so.
I would recommend implementing the long running task as a thread that notifies the user once it's done.
For more readings about the problem statement and suggested solutions:
timeout issue with chrome and flask
long request time patterns
I believe that the only thing you need is to put your connexion statement in a try/except block. So that you will be able to handle any kind of connexion error.
Furthermore, a session timeout and a connexion fail/unreachable server are different things. A session timeout disconnect a user from a server which is here for too long (usually used to avoid a user to forgot a session open). Whereas when a server is unreachable the user isn't connected so there is no session timeout.
from flask import Flask, session, app
from flask_restful import Api, Resource
from datetime import timedelta
app = Flask(__name__)
api = Api(app)
class GetParams(Resource):
def get(self):
print ("Hello.")
return 'OK'
api.add_resource(GetParams, '/data')
if __name__ == '__main__':
try:
app.run(host='130.0.1.1', port=5002)
except:
print("unexcepted error")
you could qualify the received exception, but you'll have to read a bit of doc http://flask.pocoo.org/docs/1.0/quickstart/#what-to-do-if-the-server-does-not-start

(flask + socket.IO) Result of emit callback is the response of my REST endpoint

Just to give a context here, I'm a node.JS developer, but I'm on a project that I need to work with Python using Flask framework.
The problem is, when a client request to an endpoint of my rest flask app, I need to emit an event using socket.IO, and get some data from the socket server, then this data is the response of the endpoint. But I didn't figured out how to send this, because flask needs a "return" statement saying what is the response, and my callback is in another context.
Sample of what I'm trying to do: (There's some comments explaining)
import socketio
import eventlet
from flask import Flask, request
sio = socketio.Server()
app = Flask(__name__)
#app.route('/test/<param>')
def get(param):
def ack(data):
print (data) #Should be the response
sio.emit('event', param, callback=ack) # Socket server call my ack function
#Without a return statement, the endpoint return 500
if __name__ == '__main__':
app = socketio.Middleware(sio, app)
eventlet.wsgi.server(eventlet.listen(('', 8000)), app)
Maybe, the right question here is: Is this possible?
I'm going to give you one way to implement what you want specifically, but I believe you have an important design flaw in this, as I explain in a comment above. In the way you have this coded, your socketio.Server() object will broadcast to all your clients, so will not be able to get a callback. If you want to emit to one client (hopefully not the same one that sent the HTTP request), then you need to add a room=client_sid argument to the emit. Or, if you are contacting a Socket.IO server, then you need to use a Socket.IO client here, not a server.
In any case, to block your HTTP route until the callback function is invoked, you can use an Event object. Something like this:
from threading import Event
from flask import jsonify
#app.route('/test/<param>')
def get(param):
ev = threading.Event()
result = None
def ack(data):
nonlocal result
nonlocal ev
result = {'data': data}
ev.set() # unblock HTTP route
sio.emit('event', param, room=some_client_sid, callback=ack)
ev.wait() # blocks until ev.set() is called
return jsonify(result)
I had a similar problem using FastAPI + socketIO (async version) and I was stuck at the exact same point. No eventlet so could not try out the monkey patching option.
After a lot of head bangings it turns out that, for some reason, adding asyncio.sleep(.1) just before ev.wait() made everything work smoothly. Without that, emitted event actually never reach the other side (socketio client, in my scenario)

Categories