IntegrityError: duplicate key value violates unique constraint "records_t0times_student_id_semester_id_8a0806b6_uniq" DETAIL: Key (student_id, semester_id)=(2873, 346) already exists.
View details in Rollbar: https://rollbar.com/iiuni/projektzapisy/items/491/
Traceback (most recent call last):
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/newrelic/hooks/framework_django.py", line 554, in wrapper
return wrapped(*args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 614, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 233, in inner
return view(request, *args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1719, in changelist_view
response = self.response_action(request, queryset=cl.get_queryset(request))
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1402, in response_action
response = func(self, request, queryset)
File "/home/zapisy/deploy/releases/20220913111411/zapisy/apps/enrollment/courses/admin.py", line 63, in refresh_opening_times
T0Times.populate_t0(semester)
File "/usr/lib/python3.8/contextlib.py", line 75, in inner
return func(*args, **kwds)
File "/home/zapisy/deploy/releases/20220913111411/zapisy/apps/enrollment/records/models/opening_times.py", line 111, in populate_t0
cls.objects.bulk_create(created)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/models/query.py", line 506, in bulk_create
returned_columns = self._batched_insert(
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/models/query.py", line 1271, in _batched_insert
inserted_rows.extend(self._insert(
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/models/query.py", line 1254, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1397, in execute_sql
cursor.execute(sql, params)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 66, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/newrelic/hooks/database_psycopg2.py", line 64, in execute
return super(CursorWrapper, self).execute(sql, parameters, *args,
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/newrelic/hooks/database_dbapi2.py", line 38, in execute
return self.__wrapped__.execute(sql, parameters,
IntegrityError: duplicate key value violates unique constraint "records_t0times_student_id_semester_id_8a0806b6_uniq"
DETAIL: Key (student_id, semester_id)=(2873, 346) already exists.
Traceback (most recent call last):
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/newrelic/hooks/database_psycopg2.py", line 64, in execute
return super(CursorWrapper, self).execute(sql, parameters, *args,
File "/home/zapisy/deploy/releases/20220913111411/venv/lib/python3.8/site-packages/newrelic/hooks/database_dbapi2.py", line 38, in execute
return self.__wrapped__.execute(sql, parameters,
UniqueViolation: duplicate key value violates unique constraint "records_t0times_student_id_semester_id_8a0806b6_uniq"
DETAIL: Key (student_id, semester_id)=(2873, 346) already exists.
Błąd można zreplikować uruchamiając równolegle obliczenia czasów otwarcia zapisów dla danego semestru w panelu administratora. Problem bierze się z wyścigów między powstałymi w ten sposób transakcjami. Atrakcyjnym rozwiązaniem byłoby ustawienie poziomu izolacji "serializable" dla tych transakcji i złapanie wyrzuconego wyjątku. Niestety, Django zdaje się nie oferować zbyt dobrego wsparcia dla takiego podejścia (tj. nie mamy jawnego sposobu ustawiania poziomów izolacji dla indywidualnych transakcji, ale istnieją rozszerzenia, które twierdzą, że to potrafią) . Być może warto rozważyć dodanie osobnego połączenia z bazą danych w pliku settings.py o odpowiednim poziomie izolacji. Takie połączenie mogłoby wówczas być wykorzystywane przez wybrane transakcje (np. poprzez dekorator @transaction.atomic(using='<nazwa połączenia>')). Do alternatywnych (lecz raczej nieskutecznych) podejść należy definiowanie własnych kursorów lub dedykowany nowemu połączeniu manager. Pozostaje też problem złapania wyjątku, jako że kod odpowiadający za przebieg transakcji traci sterowanie w momencie naruszenia zasad wymuszonych przez wybrany poziom izolacji.