diff --git a/contest/migrations/0010_auto_20190326_0201.py b/contest/migrations/0010_auto_20190326_0201.py new file mode 100644 index 00000000..8700e1f1 --- /dev/null +++ b/contest/migrations/0010_auto_20190326_0201.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.7 on 2019-03-26 02:01 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contest', '0009_auto_20180501_0436'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='acmcontestrank', + unique_together={('user', 'contest')}, + ), + migrations.AlterUniqueTogether( + name='oicontestrank', + unique_together={('user', 'contest')}, + ), + ] diff --git a/contest/models.py b/contest/models.py index 5d6be507..4616bef9 100644 --- a/contest/models.py +++ b/contest/models.py @@ -74,6 +74,7 @@ class ACMContestRank(AbstractContestRank): class Meta: db_table = "acm_contest_rank" + unique_together = (("user", "contest"),) class OIContestRank(AbstractContestRank): @@ -84,6 +85,7 @@ class OIContestRank(AbstractContestRank): class Meta: db_table = "oi_contest_rank" + unique_together = (("user", "contest"),) class ContestAnnouncement(models.Model): diff --git a/judge/dispatcher.py b/judge/dispatcher.py index ce4bc983..9204e542 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -187,8 +187,9 @@ class JudgeDispatcher(DispatcherBase): logger.info( "Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id) return - self.update_contest_problem_status() - self.update_contest_rank() + with transaction.atomic(): + self.update_contest_problem_status() + self.update_contest_rank() else: if self.last_result: self.update_problem_status_rejudge() @@ -329,29 +330,30 @@ class JudgeDispatcher(DispatcherBase): if self.contest.rule_type == ContestRuleType.OI or self.contest.real_time_rank: cache.delete(f"{CacheKey.contest_rank_cache}:{self.contest.id}") - with transaction.atomic(): - if self.contest.rule_type == ContestRuleType.ACM: - model = ACMContestRank - func = self._update_acm_contest_rank - else: - model = OIContestRank - func = self._update_oi_contest_rank + def get_rank(model): + return model.objects.select_for_update().get(user_id=self.submission.user_id, contest=self.contest) + if self.contest.rule_type == ContestRuleType.ACM: + model = ACMContestRank + func = self._update_acm_contest_rank + else: + model = OIContestRank + func = self._update_oi_contest_rank + + try: + rank = get_rank(model) + except model.DoesNotExist: try: - # todo unique index - # func 也不是安全的 - rank = model.objects.get(user_id=self.submission.user_id, contest=self.contest) - except ACMContestRank.DoesNotExist: - try: - rank = model.objects.create(user_id=self.submission.user_id, contest=self.contest) - except IntegrityError: - rank = model.objects.get(user_id=self.submission.user_id, contest=self.contest) - func(rank) + model.objects.create(user_id=self.submission.user_id, contest=self.contest) + rank = get_rank(model) + except IntegrityError: + rank = get_rank(model) + func(rank) def _update_acm_contest_rank(self, rank): info = rank.submission_info.get(str(self.submission.problem_id)) # 因前面更改过,这里需要重新获取 - problem = Problem.objects.get(contest_id=self.contest_id, id=self.problem.id) + problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id) # 此题提交过 if info: if info["is_ac"]: