Найти - Пользователи
Полная версия: Fork-safe...
Начало » Python для экспертов » Fork-safe...
1 2
ZZZ
У меня есть интересный вопрос… Привет всем!

Есть такой кусочек кода:
    pid = os.fork()
if pid > 0:
sys.exit(0)

sys.stdout.flush()
sys.stderr.flush()
si = open('/dev/null', 'r')
so = open('/dev/null', 'a+')
se = open('/dev/null', 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
На момент форка уже присутствует соединение с базой данных (psycopg2) и после форка это соединение активно используется. Проблем пока не заметил, но всё-таки интересно: как вообще форкаются сокеты? И что будет, если я не убью родителя, а продолжу работать с коннектом?
Андрей Светлов
Форкаются через dup, как и файлы.
Т.е. с самим сокетом проблемы нет.
Беда может прийти, если родитель и потомок пытаются выполнить запрос к БД без взаимной синхронизации. Сервер может не понять, если ему в один сокет будут
пихать одновременно два запроса. А еще у клиента базы данных есть свой state - PGconn из libpq.
При обработке запроса pg_conn изменяется. Это - локальная память процесса, синхронизации я не увидел. Т.е. выбирая, например, результаты запроса в родителе библиотека никак не сигналит об этом потомку. А pg_conn имеет внутренний буфер - примерно как FILE* является надстройкой над int file descriptor.

В итоге - порвется все к чертям.
ZZZ
Хм… Спасибо, ясно.
ZZZ
Нет. Стоп. Чего-то я запутался.
В моём примере у нас умирает родитель, в котором отрабатывается gc, который закрывает (в теории) соединение. По идее клиент должен послать серверу сообщение о том, что коннект закрыт и потомок, при попытке постучаться туда, ничего хорошего получить не должен.
Или я бред несу?
Андрей Светлов
ZZZ, я пока не знаю, что тебе ответить. Питоновская часть проста как пять копеек - и все сводится к libpg.
Я смотрел ее реализацию очень бегло - незнакомый код и все такое. Соединение закрывается на PGfinish - это верно.
Код этой функции шлет сообщение _внутренней_ системе - нужно закрыться. Что дальше - не смотрел, извини.

Если это вопрос жизни или смерти - могу попытаться понять, как эта штука ведет себя в деталях.

Ответ “в общем” - так же, как и C код, использующий libpq. Никаких неожиданностей или трюков, как я вижу.
ZZZ
Андрей Светлов
Если это вопрос жизни или смерти
Ой нет, спасибо, это больше академический интерес по принципу: а вдруг кто-нибудь знает. :-)
Всё-таки хочется понимать это лучше.

Теперь про практику. Проблема-таки нашлась. Дело в том, что после форка я делаю два запроса – сначала выборку с довольно долгим (несколько часов) перебором, а потом “update” о том, что всё отработало. Ну ещё “commit”. Трабла возникла в том, что во время апдейта или коммита (там ооочень сложно увидеть трейсбек, поэтому точнее не скажу) происходило падение и апдейт не срабатывал.
Решилось просто созданием нового коннекта после форка.
poltergeist
ZZZ
в котором отрабатывается gc, который закрывает (в теории) соединение.
Не, в теории как раз соединением владеют два процесса, и оно закроется только тогда, когда оба процесса освободят этот ресурс.

З.Ы. Хотя мне не мешало бы освежить свои знания из области системного программирования, могу в чём-то ошибаться.
ZZZ
poltergeist
Не, в теории как раз соединением владеют два процесса, и оно закроется только тогда, когда оба процесса освободят этот ресурс.
С точки зрения сокета да, но с точки зрения более высокоуровневой части… Я думаю, что в норме соединение должно закрываться не убийством сокета, а посыланием пакета (сообщения), говорящим о том, что соединение закончено. Вот тут и проблема. :-)
Андрей Светлов
ZZZ
С моей точки зрения не стоит надеятся, что fork нормально отработает на объектах сложнее файла, сокета или shared memory. Т.е. новое подключение в потомке - это идеологически правильно.
fork отлично работает с объектами уровня ядра. Если поверх этого объекта (а то и нескольких - канал связи, объекты синхронизации) что-то надстроили в user space - результат в общем случае непредсказуемый. Если только заранее не была предусмотрена эта возможность.
ZZZ
Ну вот где-то так я и представил. :-)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB