我使用带有两个工作线程和nginx的uwsgi,在使用MySQL的flask-sqlalchemy中导致PK违规的并发问题。
我使用来自有效载荷的密钥。如果联系人带有数据库中存在的密钥,则更新记录,否则,创建新记录。
这是设置和我认为正在发生的事情。
#project/__init__.py
app = Flask(__name__)
db = SQLAlchemy(app)
from project import views, models
#project/views.py
from project import db
@app.route('/receive', methods = ['POST'])
def receive():
#Check to see if the contact exists in the database
contact = models.Contact.getIssue(contact_payload['id'])
if contact is None:
#If not in the DB, create a new contact
new_contact = models.Contact(contact_payload)
db.session.merge(new_contact)
else:
#If in the DB, create an updated representation of this contact
updated_issue = contact.updateContact(contact_payload)
db.session.merge(updated_issue)
...Some other stuff...
#Commit to DB
db.session.commit()
#Respond
resp = jsonify({'Result' : 'Success'})
resp.status_code = 200
return resp
当我们在同一时间收到同一联系人的两个请求时,问题就出现了(requestA是12:10:49,063,requestB是12:10:49,066)。其中一个请求以PK违规结束。
我怀疑他们会进入不同的工作线程,每个请求都会从flask-sqlalchemy获得一个范围会话(sessionA - requestA和sessionB - requestB)。
我怀疑两个会话在上面代码流中同时出现的请求开始时什么都不包含。
requestA遍历相应的代码流并将new_contact合并到sessionA中。
同时requestB通过相同的代码路径,其中contact是None(因为sessionA还没有提交)并将new_contact合并到sessionB中。
然后sessionA在sessionB之前提交。当sessionB进入提交时,它会获得PK违规。
除了捕获PK违规并做出相应反应之外,我们还能做其他事情吗?
答案 0 :(得分:0)
您有两种选择:
抓住违反PK的行为,并做出相应的反应,就像你已经说过的那样。
根据你的id锁定你的交易:这个更复杂,你需要一些东西来同步你的锁,比如redis。看看python-redis-lock。它只是一个选项,这里的解决方案是避免PK的并发性。