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

yya*_*ian 5 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)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

如果用户 1 和用户 2 同时对同一商品下订单,并且还剩 5 件商品。也许 user1 的请求首先到达 3 个订单。那么当user2的请求到达时,user1的订单可能还没有提交。所以 user2 还看到了 5 个项目。所以最后,数据库中剩下的项目是2;user1 和 user2 都成功地为每个用户下了 3 件商品的订单,尽管一开始只剩下 5 件商品,这已经足够了。如何避免这个问题?

这张图片解释了上述情况。

dir*_*irn 3

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

item.count = Item.count - order
Run Code Online (Sandbox Code Playgroud)

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

这可以很简单

db.session.commit()

if item.count < 0:
  # roll back the order 
Run Code Online (Sandbox Code Playgroud)

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