Django/PostgreSQL параллельное одновременное увеличение значения поля с помощью транзакции вызывает OperationalError

У меня есть модель, связанная с некоторым количеством записей. Каждая запись в одной связке должна иметь свою уникальную версию (в порядке создания записей в БД).

Как по мне, лучший способ сделать это - использовать транзакции, но я столкнулся с проблемой параллельного выполнения блоков транзакций. Когда я удаляю блок transaction.atomic(), все работает, но версии не обновляются после выполнения.

Я написал небольшой кусок кода для тестирования одновременного увеличения версии записи в базе данных:

def _save_instance(instance):
    time = random.randint(1, 50)
    sleep(time/1000)

    instance.text = str(time)
    instance.save()


def _parallel():
    instances = MyModel.objects.all()

    # clear version
    print('-- clear old numbers -- ')
    instances.update(version=None)

    processes = []
    for instance in instances:
        p = Process(target=_save_instance, args=(instance,))
        processes.append(p)

    print('-- launching -- ')
    for p in processes:
        p.start()

    for p in processes:
        p.join()

    sleep(1)
    ...
    # assertions to check if versions are correct in one bunch

    print('parallel Ok!')

метод save() в MyModel определен следующим образом:

...
def save(self, *args, **kwargs) -> None:
        with transaction.atomic():
            if not self.number and self.banch_id:

                max_number = MyModel.objects.filter(
                    banch_id=self.banch_id
                ).aggregate(max_number=models.Max('version'))['max_number']

                self.version = max_number + 1 if max_number else 1

            super().save(*args, **kwargs)

Когда я запускаю свой тестовый код на случайном количестве записей (30-300), я получаю ошибку:

django.db.utils.OperationalError: server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.

После этого все процессы стекируются, и я могу остановить скрипт только с помощью KeyboardInterrupt.

Полная трассировка стека процесса:

Process Process-14:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
psycopg2.OperationalError: server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/usr/local/lib/python3.6/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "/app/scripts/test_concurrent_saving.py", line 17, in _save_instance
    instance.save()
  File "/app/apps/incident/models.py", line 385, in save
    ).aggregate(max_number=models.Max('version'))['max_number']
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 384, in aggregate
    return query.get_aggregation(self.db, kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py", line 503, in get_aggregation
    result = compiler.execute_sql(SINGLE)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1152, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/raven/contrib/django/client.py", line 123, in execute
    return real_execute(self, sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.OperationalError: server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.

В чем причина такого поведения? Буду благодарен за любую помощь или совет!

Вернуться на верх