当一个SQLAlchemy并发更新同一条记录时出错了什么?

时间:2014-10-14 08:33:01

标签: mysql flask sqlalchemy

我的项目(flask + sqlalchemy)由uWSGI(4名工作人员)和mysql(使用InnoDB)部署。

这些是我的模特:

class Cards(db.Model):
    id             =db.Column(db.Integer, primary_key = True)
    no             =db.Column(db.String(11),index=True, unique = True,nullable=False)
    balance        =db.Column(db.Numeric(12,2),nullable=False,default=0)


class trans_details(db.Model):
    id             =db.Column(db.Integer, primary_key = True)
    from_card_id   =db.Column(db.Integer, db.ForeignKey('cards.id'),nullable=False)
    to_card_id     =db.Column(db.Integer, db.ForeignKey('cards.id'),nullable=False)
    amount         =db.Column(db.Numeric(12,2),nullable=False)

    from_card_balance=db.Column(db.Numeric(12,2),nullable=False)
    to_card_balance  =db.Column(db.Numeric(12,2),nullable=False

    timestamp      =db.Column(db.Numeric(17,7),default=time.time,nullable=False)#时间戳

    from_card      =db.relationship('Cards',foreign_keys=[from_card_id],
                    backref=db.backref('out_details',lazy='dynamic'))
    to_card        =db.relationship('Cards',foreign_keys=[to_card_id],
                    backref=db.backref('in_details',lazy='dynamic'))

我的代码是这样的:

@contextmanager
def trans():
    try:
        yield
        db.session.commit()
    except:
        db.session.rollback()

def transfer(from_card,to_card,amount):
    with trans():
        from_card.balance=Cards.balance-amount
        to_card.balance=Cards.balance+amount

        db.session.add(from_card)
        db.session.add(to_card)
        db.session.flush()

        if from_card.balance<0:
            raise Exception('xxx')

        details=trans_details(from_card=from_card,
                          to_card=to_card,
                          amount=amount,
                          from_card_balance=from_card.balance,
                          to_card_balance=to_card.balance)
        db.session.add(details)
        db.session.flush()

def batch_transfer(rule):
    with trans():
        #parse the rule and call transfer function time after time

但有时候我没有得到预期的结果,例如:

select * from trans_details order by `timestamp` desc;

|from_card |to_card |amount |from_card_balance       |to_card_balance |timestamp
A          C        100     1000(This Should be 950) xxx              1413257244.5339300(2014-10-14 11:27:24)
A          B        50      1050                     150              1413257244.4818400(2014-10-14 11:27:24)
B          A        100     100                      1100             xxx

我该如何解决?

1 个答案:

答案 0 :(得分:1)

我修好了。是我的问题,我的代码如下:

from_card=Cards.query.with_for_update().filter(xxx)
to_card=Cards.query.with_for_update().filter(xxx)
#call a function here,but in the function have a commit operation so..
transfer(from_card,to_card,amount)