Web applications 在线优惠券生成应用程序中的竞争条件

Web applications 在线优惠券生成应用程序中的竞争条件,web-applications,race-condition,Web Applications,Race Condition,假设我有一个应用程序,其中用户必须输入一个刮刮卡代码,然后将1000美元转账到他的帐户 现在,我的应用程序将侦听代码。这样的事情会发生- def RedeemCode(code, user_id): isvalid = checkValidCode(code) # queries database if isvalid is True: setCodeAsExpired(code) # updates database addMoneyToAcco

假设我有一个应用程序,其中用户必须输入一个刮刮卡代码,然后将1000美元转账到他的帐户

现在,我的应用程序将侦听代码。这样的事情会发生-

def RedeemCode(code, user_id):
    isvalid = checkValidCode(code) # queries database
    if isvalid is True:
        setCodeAsExpired(code) # updates database
        addMoneyToAccount(amount=1000, id=user_id)
假设两个请求以相同的
code
user\u id
同时出现。还假设
checkValidCode
为这两种代码返回
True
,因为执行顺序为:

checkValidCode[request1]->checkValidCode[request2]->setCodeAsExpired[request1]->setCodeAsExpired[request2]

通过这种方法,存在两次贷记资金的问题

在这种情况下,如何设计系统来防止此类问题?
(如果这是一个小问题,很抱歉。我正在考虑这个系统将如何工作,但在这一点上被卡住了)

这就是您需要锁和事务设计(原子)的地方。您希望检查有效性、使代码过期以及将用户记入贷方的过程成为单个原子的、不可分割的事务

然而,您创建的这个锁定的、不可分割的事务因语言/库/框架而异

因为您展示的代码是用Python编写的,所以甚至不可能并发执行它(请参阅Python的全局解释器锁)

在Java中,在锁定整个对象的方法上有一个
synchronized
关键字,因此它不仅会阻止任何其他线程同时执行该特定方法的代码,而且还会阻止它们调用同一对象中的任何其他同步方法

在C#中,您可以管理自己对资源的锁,但不需要单独的锁特定对象类型。有一个带有
lock
关键字的语言级支持,但它比Java设计更细粒度。这既有好的一面,也有坏的一面:能够更加谨慎地锁定东西对效率有好处,但对安全性和更容易发生人为错误也有坏处

在更多的“卷起袖子和脏”硬件级语言如C和C++中,你经常把关键部分处理成单独的对象类型,根据你选择的粒度级别而不是仅仅是整个对象来锁定适当的代码段。在这些情况下,人们甚至可以深入到比较和交换(CAS)互锁原子操作的级别,以确保即使像递增整数这样的操作也是一个原子事务(实际上在硬件级别上它不一定是原子事务,两个线程试图递增同一整数实际上可能导致竞争条件)

请注意,锁可能非常昂贵,在最坏的情况下,由于等待访问高度竞争的共享资源时挂起的线程数量较多,因此会有效地将系统性能转化为比单线程最差的性能。这可能会造成线程交通堵塞,使他们陷入瓶颈,等待轮到他们

因此,有很多策略可以对此进行优化,包括无锁设计(有时甚至无需等待)。有时也有办法分割共享资源

例如,您的客户数据库可能被拆分为单独的数据块,只有当来自同一客户的两个请求进入时才需要锁定。通过这种方式,您可以将一个潜在的巨大共享资源转换为多个可以单独锁定的资源

你似乎想知道锁是否会超时。有些设计允许这样做,但如果允许的话,它们总是朝着忽略执行关键部分的方向超时(基本上是朝着中止尝试的方向出错)。如果线程能够在并发执行临界段的一侧超时,那么它将破坏整个锁定点。因此,当您在某个对象上加上锁时,您有一个强有力的保证,即没有两个线程会同时执行该代码


你要研究的是线程锁,它们是如何工作的,以及事务编码和思维的一般思维方式。这对于异常和错误处理也是重要的,以确保如果在关键操作的中间遇到错误,它不会使系统处于“半完成”无效状态。事务应整体成功,或整体中止/回滚。

当数据库是应用程序的真实来源时,在数据库级别强制执行具有约束的数据完整性非常重要。正如我所展示的,在某些情况下,验证将通过,即使最终结果一旦持久化是无效的。在最坏的情况下,这是一个严重的安全缺陷,即使在最好的情况下,它也会导致数据完整性问题。简单的解决方案是在模型的外键和优惠券代码字段的组合上添加一个唯一的索引,这样数据库服务器将拒绝重复项,因为现代数据库服务器都是原子和线程安全的。

使用锁。。。如果编程语言支持多线程,那么应该可以对对象或方法设置锁。您还可以向数据库中添加锁,以防止相同的值被更新两次。@Darwind那么锁定方法的效果如何?它会锁定一个方法还是一个方法和args的组合?DB锁将如何工作?通常情况下,当你在写的时候,你会锁定其他人,这样其他人就不能写,或者其他人也不能读。当你锁定一个方法或对象时,其他调用方会被告知等待,直到轮到他们为止。例如,如果锁定一个方法,第一个调用方必须在其他任何人开始运行该方法之前离开该方法。有不同的方法来执行数据库锁定。有些使用乐观并发控制,有些使用悲观并发控制。可能还有其他方法来处理并发性。你问了一个非常“抽象”的问题,所以你得到了一个抽象的答案