我正在运行一个比赛网站,您尝试在该网站上点击X号以赢得奖金.它是用Django编写的,并在Postgresql的Heroku上运行.
每次点击都保存为Play模型的实例,该实例通过查看数据库中之前有多少次播放来计算其编号,并加1.此数字保存在Play模型中.这对整个站点至关重要,因为您打什么号码决定您是否获得奖金.
最近,我们有一个案例,其中有2个人同时获得中奖号码.查看数据库,我发现实际上大约有3%的戏剧分享他们的数字.哎呀.
我在Play模型的“数字”和“游戏”字段中添加了“ unique_together”,因此数据库将帮助我避免将来出现重复的数字,但担心未来的比赛条件可能会使系统跳过某些数字,这会如果所讨论的数字在哪里赢了,那就不好了.
我已经研究过锁定表,但是担心这可能会破坏站点的并发性(我们目前有多达500个同时播放器,并且将来还会更多).
我也应该采取什么策略才能100%确保自己从未重复或跳过数字?
我的游戏课:
class Play(models.Model):
token = models.CharField(unique=True, max_length=200)
user = models.ForeignKey(User)
game = models.ForeignKey(Game)
datetime = models.DateTimeField(auto_Now_add=True)
winner = models.BooleanField(default=False)
flagged = models.BooleanField(default=False)
number = models.IntegerField(blank=True, null=True)
ip = models.CharField(max_length=200, blank=True, null=True)
def assign_number(self, x=1000):
#try to assign number up to 1000 times, in case of race condition
while x > 0:
before = Play.objects.filter(id__lt=self.id, game=self.game)
try:
self.number = before.count()+1
self.save()
x=0
except:
x-=1
class Meta:
unique_together = (('user', 'game', 'datetime'), ('game','number'))
解决方法:
一个简单的解决方案是将柜台用户和获胜者用户置于游戏模型中.然后,您可以使用select_for_update锁定记录:
game = Game.objects.select_for_update().get(pk=gamepk)
if game.number + 1 == X
# he is a winner
game.winner = request.user
game.number = game.number + 1
game.save()
else:
# u might need to stop the game if a winner already decided
作为同一笔交易的一部分,您还可以记录Player的对象,以便您也知道单击了谁并跟踪其他信息,但不要在其中放置数字和获胜者.要使用select_for_update,您需要使用postgresql_psycopg2后端.
更新:
由于django默认情况下将autocommit设置为on,因此您必须将以上代码包装在原子事务中.从Django docs起
Select for update
If you were relying on “automatic transactions” to provide locking between select_for_update() and a subsequent >write operation — an extremely fragile design, but nonetheless possible —
you must wrap the relevant code in atomic().
您可以使用@ transaction.atomic装饰视图:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。