Задний план:
Я работаю над проектом, который использует Django с базой данных Postgres. Мы также используем mod_wsgi в случае, если это имеет значение, так как некоторые из моих поисковых запросов упоминали об этом. При отправке веб-формы представление Django запускает работу, которая займет значительное количество времени (больше, чем хотелось бы ждать пользователю), поэтому мы запускаем работу с помощью системного вызова в фоновом режиме. Выполняемое задание должно иметь возможность чтения и записи в базу данных. Поскольку это задание занимает много времени, мы используем многопроцессорность для параллельного выполнения его частей.
Проблема:
У сценария верхнего уровня есть соединение с базой данных, и когда он порождает дочерние процессы, кажется, что соединение родителя доступно для детей. Затем есть исключение о том, как SET TRANSACTION ISOLATION LEVEL должен вызываться перед запросом. Исследования показали, что это происходит из-за попытки использовать одно и то же соединение с базой данных в нескольких процессах. Один поток, который я обнаружил, предлагал вызвать connection.close () в начале дочерних процессов, чтобы Django автоматически создавал новое соединение, когда оно ему нужно, и, следовательно, каждый дочерний процесс будет иметь уникальное соединение, то есть не разделяемое. У меня это не сработало, так как вызов connection.close () в дочернем процессе заставил родительский процесс пожаловаться на потерю соединения.
Другие результаты:
Некоторые вещи, которые я прочитал, похоже, указывают на то, что вы действительно не можете этого сделать, и что multiprocessing, mod_wsgi и Django плохо работают вместе. Мне кажется, в это трудно поверить.
Некоторые предлагали использовать сельдерей, что могло бы быть долгосрочным решением, но я не могу установить сельдерей в настоящее время, ожидая некоторых процессов утверждения, поэтому сейчас это не вариант.
Нашел несколько ссылок на SO и в других местах о постоянных соединениях с базой данных, что, по моему мнению, является другой проблемой.
Также нашел ссылки на psycopg2.pool и pgpool и кое-что о вышибале. По общему признанию, я не понимал большей части того, что читал по ним, но это определенно не бросалось мне в глаза как то, что я искал.
Текущий «Рабочий процесс»:
На данный момент я вернулся к последовательному запуску вещей, и это работает, но медленнее, чем хотелось бы.
Любые предложения относительно того, как я могу использовать многопроцессорную обработку для параллельной работы? Похоже, что если бы у меня был родитель и двое детей, которые имели бы независимые подключения к базе данных, все было бы хорошо, но я не могу получить такое поведение.
Спасибо и извините за длину!
django.db.connections.close_all()
чтобы закрыть все соединения одним звонком.При использовании нескольких баз данных следует закрыть все соединения.
from django import db for connection_name in db.connections.databases: db.connections[connection_name].close()
РЕДАКТИРОВАТЬ
Используйте то же, что и @lechup, чтобы закрыть все соединения (не уверен, с какой версии django был добавлен этот метод):
from django import db db.connections.close_all()
источник
alias
илиinfo
вfor
теле цикла, еслиdb
илиclose_connection()
поддерживает это.Для Python 3 и Django 1.9 это сработало для меня:
import multiprocessing import django django.setup() # Must call setup def db_worker(): for name, info in django.db.connections.databases.items(): # Close the DB connections django.db.connection.close() # Execute parallel code here if __name__ == '__main__': multiprocessing.Process(target=db_worker)
Обратите внимание, что без django.setup () я не смог бы заставить это работать. Я предполагаю, что что-то нужно снова инициализировать для многопроцессорной обработки.
источник
setup
django.db.connections.databases.items()
- он просто несколько раз закрывает соединение.db.connections.close_all()
работает нормально, пока называется рабочей функцией.У меня были проблемы с "закрытым соединением" при последовательном запуске тестовых примеров Django . Помимо тестов, существует еще один процесс, намеренно изменяющий базу данных во время выполнения теста. Этот процесс запускается в каждом тестовом примере setUp ().
Простым решением было унаследовать мои тестовые классы от
TransactionTestCase
вместоTestCase
. Это гарантирует, что база данных действительно была записана, а другой процесс имеет актуальное представление о данных.источник
(не лучшее решение, но возможное решение)
Если вы не можете использовать сельдерей, возможно, вы могли бы реализовать свою собственную систему очередей, в основном добавляя задачи в какую-то таблицу задач и имея обычный cron, который их выбирает и обрабатывает? (через команду управления)
источник
Привет, я столкнулся с этой проблемой и смог решить ее, выполнив следующие действия (мы реализуем ограниченную систему задач)
task.py
from django.db import connection def as_task(fn): """ this is a decorator that handles task duties, like setting up loggers, reporting on status...etc """ connection.close() # this is where i kill the database connection VERY IMPORTANT # This will force django to open a new unique connection, since on linux at least # Connections do not fare well when forked #...etc
ScheduledJob.py
from django.db import connection def run_task(request, job_id): """ Just a simple view that when hit with a specific job id kicks of said job """ # your logic goes here # ... processor = multiprocessing.Queue() multiprocessing.Process( target=call_command, # all of our tasks are setup as management commands in django args=[ job_info.management_command, ], kwargs= { 'web_processor': processor, }.items() + vars(options).items()).start() result = processor.get(timeout=10) # wait to get a response on a successful init # Result is a tuple of [TRUE|FALSE,<ErrorMessage>] if not result[0]: raise Exception(result[1]) else: # THE VERY VERY IMPORTANT PART HERE, notice that up to this point we haven't touched the db again, but now we absolutely have to call connection.close() connection.close() # we do some database accessing here to get the most recently updated job id in the database
Честно говоря, чтобы предотвратить состояние гонки (с несколькими одновременными пользователями), было бы лучше вызвать database.close () как можно быстрее после того, как вы разветвите процесс. Тем не менее, все еще может быть вероятность, что другой пользователь где-то в конце линии полностью сделает запрос к db, прежде чем у вас будет возможность очистить базу данных.
Честно говоря, было бы безопаснее и разумнее, если бы вилка не вызывала команду напрямую, а вместо этого вызывала сценарий в операционной системе, чтобы порожденная задача выполнялась в собственной оболочке django!
источник
Вы можете предоставить Postgre больше ресурсов, в Debian / Ubuntu вы можете редактировать:
nano /etc/postgresql/9.4/main/postgresql.conf
заменив 9.4 вашей версией postgre.
Вот несколько полезных строк, которые для этого следует обновить примерами значений, названия говорят сами за себя:
max_connections=100 shared_buffers = 3000MB temp_buffers = 800MB effective_io_concurrency = 300 max_worker_processes = 80
Будьте осторожны, не увеличивайте эти параметры слишком сильно, так как это может привести к ошибкам, когда Postgre попытается использовать больше ресурсов, чем доступно. Приведенные выше примеры отлично работают на машине Debian с 8 ГБ оперативной памяти, оснащенной 4 ядрами.
источник
Если вам нужен только параллелизм ввода-вывода, а не параллелизм обработки, вы можете избежать этой проблемы, переключив свои процессы на потоки. Заменить
from multiprocessing import Process
с участием
from threading import Thread
Thread
Объект имеет тот же интерфейс,Procsess
источник
Если вы также используете пул соединений, у нас сработало следующее: принудительное закрытие соединений после разветвления. Раньше вроде не помогало.
from django.db import connections from django.db.utils import DEFAULT_DB_ALIAS connections[DEFAULT_DB_ALIAS].dispose()
источник