如何使用flask-sqlalchemy确保在竞争条件下一致的数据库更新

时间:2016-01-23 19:32:54

标签: python database postgresql flask-sqlalchemy

这可能是个老问题。但我是数据库和flask-sqlalchemy的新手,无法找到之前帖子的任何答案。也许答案很简单。

我正在尝试使用postgresql将Flask应用程序部署到Heroku。它具有以下模型,即跟踪库存中剩余项目的库存。

class Inventory(db.Model):
    __tablename__ = 'inventories'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    count = db.Column(db.Integer)

有一个网络表单,用户可以使用该表单下订单,该表单将更新按字段计数跟踪的数据库中剩余的项目数。下面的代码处理订单并根据是否有足够的项目设置不同的状态。

item = Inventory.query.filter_by(name=form.name.data).first()
order = int(form.count.data)
if item.count > order:
    item.count -= order
    db.session.add(item)
    db.session.commit()
    status = ('%r: %d left' % (item.name, item.count))
else:
    status = '%r not enough left' % item.name

如果user1和user2同时在同一个商品上下订单,则剩下5个商品。也许user1的请求首先以3个订单到达。然后当user2的请求到达时,用户1的订单可能尚未提交。因此user2还会看到5个项目。所以最后,数据库中剩下的项目是2; user1和user2都成功地为每个用户下了3个项目的订单,尽管开头只剩下5个项目,这是足够的。如何避免这个问题?

This image explains the above situation.

2 个答案:

答案 0 :(得分:2)

要防止过时数据泄漏到数据库中,您需要执行原子更新。

item.count = Item.count - order

提交会话后,您仍然需要检查并处理负数量。

这可以像

一样简单
db.session.commit()

if item.count < 0:
  # roll back the order 

提交会话时,SQLAlchemy会清除其内部缓存并从数据库重新加载记录。

答案 1 :(得分:0)

这是@dirn答案的完整代码示例

item = Inventory.query.filter_by(name=form.name.data).first()
order = int(form.count.data)
item.count = Item.count - order
db.session.commit()

if item.count < 0:
    status = '%r not enough left' % item.name

    # Should rollback/cancel the order
    ...

else:
    status = ('%r: %d left' % (item.name, item.count))