Приложение Django выходит из строя при развертывании.Расовые условия? - PullRequest
0 голосов
/ 18 марта 2019

Я написал приложение django для викторины, и оно проверяет ответы пользователя и обновляет оценки, как только пользователь отправляет ответ. Вот соответствующий вид для этого -

current_question_key = 0 #This is a global variable.
def check_answer(request):
    current_user = request.user
    current_team = Team.objects.get(user = current_user)
    current_score = current_team.score

    if request.method == "POST":
        answer = request.POST.get('answer')
        question = Question.objects.get(id = current_question_key)
        if answer == question.answer:
            if question in current_team.questions_answered.all(): #This is required to prevent the score from increasing if the somebody submits a correct answer to the same question more than once
                pass
            else:
                current_team.score = current_score + question.score_increment
                current_team.questions_answered.add(question)
                current_team.save()        
        else:
            # This is required to prevent the score from decreasing if someone has answered it correctly earlier
            if question in current_team.questions_answered.all():
                pass
            else :
                current_team.score = current_score - question.score_increment//negative_marking_factor
                current_team.save()
        return HttpResponse(status=204) #This means that the server has successfully processed the request and is not going to return any data.
    else:
        return HttpResponse("Error404")

Значение current_question_key изменено по сравнению с представлением, используемым для отправки вопроса во внешний интерфейс -

def game(request):   
    if request.method == "POST":
        key = request.POST.get('questionKey')
        global current_question_key
        current_question_key = key
        question = Question.objects.get(id = key)
        question_text = question.question_text
        data = {
            'question_text':question_text
        }
        return JsonResponse(data)
    else:
        current_user = request.user
        current_team = Team.objects.get(user = current_user)
        score = current_team.score
        name = current_user.username
        return render(request, 'Base/main.html', {'teamname':name, 'score':score})

При тестировании на сервере разработки django это работало отлично, даже когда его использовали одновременно около 10 человек. Но, как только я попытался обслужить его с помощью nginx (размещенного на моем ноутбуке с 5 одновременными пользователями), приложение стало бесполезным, и даже правильные ответы были оценены как неправильные.

Я тоже попробовал apache и у меня была такая же проблема Почти все запросы были обработаны неправильно. Может ли это быть связано с условиями гонки? Что именно здесь может происходить?

1 Ответ

0 голосов
/ 19 марта 2019

Вы не можете использовать такую ​​глобальную переменную в Django. Приложение Django обычно выполняется в нескольких серверных процессах, которые не разделяют память. Вызов game установил бы глобальную переменную current_question_key только в одном из процессов. Все остальные процессы будут иметь старые значения. Поскольку запрос может быть обработан любым процессом, вы получите более или менее случайные результаты.

Сервер разработки Django использует многопоточность вместо многопроцессорной обработки. Потоки, в отличие от процессов, совместно используют одну и ту же память, поэтому все запросы видят одно и то же значение для current_question_key.

Вы должны хранить current_question_key для каждого пользователя таким образом, чтобы он был доступен для всех процессов. Наиболее очевидным решением было бы сохранить эту информацию в сеансе пользователя :

request.session['current_question_key'] = ...

Кроме того, вы можете сохранить его в базе данных, например, с ForeignKey в пользовательской пользовательской модели или если вы хотите отслеживать какие-либо игры в отдельной таблице, например:

from django.contrib.auth import get_user_model
from django.db import models

class Game(model.Model)
    user = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE
    )
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)

Затем вы можете получить текущую игру для пользователя, отсортировав по дате создания:

Game.objects.filter(user=request.user).order_by('-created_at').first()

В зависимости от того, как часто меняется текущий вопрос, вы также можете рассмотреть возможность использования значения ключа, такого как Redis, хотя это немного усложняет.

...