I have a flask application and I want most requests to run concurrently. I set app.run(threaded=True)
and this seems to work for the most part. However, there's this one endpoint that runs dredd where I want to restrict it so that requests for that endpoint do not run concurrently. It appears to cause a socket error when more than one is run at the same time. Is there a way to do this?
I also faced the same kind of issue when I was working on a flask server to update/insert collection in MongoDB. I wanted to implement autoincrement id's in the MongoDB collection. we created an API route for the same and wrote MongoDB queries to get the first doc in reverse sorted the document and then added 1 and inserted a new doc with the id just calculated.
result = db_pymongo.academy.aggregate(
[
{
'$sort': {
'academy_id': -1
}
}, {
'$limit': 1
}, {
'$project': {
'academy_id': 1
}
}
]
)
# predefining academy_id if no document initially
academy_id = 1
for doc in result:
if doc:
academy_id = doc['academy_id'] + 1
new_academy = {
"academy": academy,
'academy_id': academy_id,
"players": [],
"tournaments": [],
"created_at": datetime.utcnow(),
}
db_pymongo.academy.insert_one(new_academy)
everything worked fine this way but when we sent multiple simultaneous requests to the same API route, it started misbehaving and gave the same ids to multiple inserted objects (which will obviously happen), so we tried different ways to do the same thing. we created a variable in the app.py file like this
LOCK_STATUS = {
"academy": False
}
now I created a decorator to check and update status as per need
def db_lock(name):
def wrapper(f):
@wraps(f)
def wrapped(*args, **kwargs):
while LOCK_STATUS[name]:
time.sleep(0.5)
LOCK_STATUS[name] = True
# call function and set back to false
try:
return f(*args, **kwargs)
except Exception as e:
LOCK_STATUS[name] = False
if isinstance(e, ValueError):
return jsonify({'message': e.args[0], 'type': 'ValueError'}), 400
elif isinstance(e, AttributeError):
return jsonify({'message': e.args[0], 'type': 'AttributeError'}), 400
elif isinstance(e, KeyError):
return jsonify({'message': e.args[0], 'type': 'KeyError'}), 400
elif isinstance(e, TypeError):
return jsonify({'message': e.args[0], 'type': 'TypeError'}), 400
else:
return jsonify({'message': str(e), 'type': 'InternalServerError'}), 500
finally:
LOCK_STATUS[name] = False
return wrapped
return wrapper
this workes in a way that it will check for status, if the status is false then it will proceed normally otherwise it will keep waiting until previous request releases the lock, the then-current request will acquire the lock by changing status again as true, and then the rest code will execute normally, if anything happens worng then also it will release the lock
for using this decorator we just need to pass "name" as args in (in this case it is academy) here is the full code after using db_lock decorator with API
@academy_api.route('', methods=['POST'])
@db_lock('academy')
@roles_required('admin')
def addAcademy():
data = request.get_json()
academy = data["academy"]
# increment academy id by 1
result = db_pymongo.academy.aggregate(
[
{
'$sort': {
'academy_id': -1
}
}, {
'$limit': 1
}, {
'$project': {
'academy_id': 1
}
}
]
)
academy_id = 1
for doc in result:
if doc:
academy_id = doc['academy_id'] + 1
new_academy = {
"academy": academy,
'academy_id': academy_id,
"players": [],
"tournaments": [],
"created_at": datetime.utcnow(),
}
db_pymongo.academy.insert_one(new_academy)
returnableObject(new_academy)
return jsonify(message="success", payload=new_academy), 200
it works perfectly fine and won't create problem to any other route, that means other API routes can operate simultaneously in threaded mode
I don't think it is possible to do per-route declaratively.
You can, though, use a threading.lock
or, better, a multiprocessing.lock
to serialize actual processing of something you need processed serially. Request handlers will wait until they can acquire the lock. Be sure to pay a lot of attention to releasing the lock in various error scenarios as well as during normal execution.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.