Запросы на синхронизацию на нескольких серверах
У нас есть API для игры на Django Rest Framework. В игре есть турниры и участники турниров. Модель турнира имеет поле max_players и API не должен позволять регистрировать больше участников, когда число max_players достигнуто.
Это упрощенные модели:
class Tournament(models.Model):
max_players = models.PositiveIntegerField()
class TourneyPlayer(models.Model):
tourney = models.ForeignKey(Tourney, on_delete=models.CASCADE, related_name='players')
user = models.ForeignKey(User, on_delete=models.CASCADE)
В конечной точке регистрации установлен сериализатор, который в своем методе validate проверяет количество уже зарегистрированных игроков и возвращает ошибку, если турнир переполнен:
def validate(self, attrs):
if tourney.players.count() >= tourney.max_players:
raise serializers.ValidationError()
return attrs
Если проверка пройдена, мы создаем новый объект TourneyPlayer.
Но когда мы создаем новый турнир, все пользователи получают уведомление, и многие из них сразу же нажимают на кнопку регистрации. Это приводит к большому количеству одновременных запросов, и иногда игрок регистрируется даже тогда, когда счетчик max_player достигнут. Это происходит потому, что существует некоторая задержка между проверкой и созданием, поэтому возникает классическая проблема параллелизма:
Запрос A подтвержден Запрос B подтвержден Запрос A создал игрока Запрос B создал игрока
Локально я решил эту проблему, добавив объект threading.Lock, но как это сделать, если у нас несколько серверов за балансировщиком нагрузки? Я думаю использовать Redis, записывать некоторое значение, когда начинается запрос на регистрацию, и очищать это значение, когда он завершается. А если значение уже установлено, проверять его в цикле с некоторым интервалом, используя функцию sleep. Но может быть есть решения получше?