Avoiding race conditions, Django + Heroku + PostgreSQL

前端 未结 1 1671
南方客
南方客 2021-01-17 03:54

I\'m running a contest site where you try to make click number X to win a prize. It\'s written in Django and running on Heroku with PostgreSQL. Each click is saved as an ins

1条回答
  •  时光说笑
    2021-01-17 04:29

    A simple solution would be putting the counter and winner user in the Game model. You can then use select_for_update to lock the record:

    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
    

    As part of the same transaction you can also record Player s objects so you also know who clicked and track other info but don't put the number and winner there. To use select_for_update you need to use postgresql_psycopg2 backend.

    Update: Since django set autocommit on by default, you have to wrap the above code in atomic transaction. From 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().

    You can decorate your view with @transaction.atomic:

    from django.db import transaction
    
    @transaction.atomic
    def viewfunc(request):
        # This code executes inside a transaction.
        do_stuff()
    

    0 讨论(0)
提交回复
热议问题