Уведомления

Группа в Telegram: @pythonsu

#1 Июнь 9, 2010 11:56:04

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Fork-safe...

У меня есть интересный вопрос… Привет всем!

Есть такой кусочек кода:

    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) и после форка это соединение активно используется. Проблем пока не заметил, но всё-таки интересно: как вообще форкаются сокеты? И что будет, если я не убью родителя, а продолжу работать с коннектом?



Офлайн

#2 Июнь 9, 2010 16:53:51

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Fork-safe...

Форкаются через dup, как и файлы.
Т.е. с самим сокетом проблемы нет.
Беда может прийти, если родитель и потомок пытаются выполнить запрос к БД без взаимной синхронизации. Сервер может не понять, если ему в один сокет будут
пихать одновременно два запроса. А еще у клиента базы данных есть свой state - PGconn из libpq.
При обработке запроса pg_conn изменяется. Это - локальная память процесса, синхронизации я не увидел. Т.е. выбирая, например, результаты запроса в родителе библиотека никак не сигналит об этом потомку. А pg_conn имеет внутренний буфер - примерно как FILE* является надстройкой над int file descriptor.

В итоге - порвется все к чертям.



Офлайн

#3 Июнь 9, 2010 17:20:14

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Fork-safe...

Хм… Спасибо, ясно.



Офлайн

#4 Июнь 9, 2010 17:27:45

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Fork-safe...

Нет. Стоп. Чего-то я запутался.
В моём примере у нас умирает родитель, в котором отрабатывается gc, который закрывает (в теории) соединение. По идее клиент должен послать серверу сообщение о том, что коннект закрыт и потомок, при попытке постучаться туда, ничего хорошего получить не должен.
Или я бред несу?



Офлайн

#5 Июнь 11, 2010 22:38:39

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Fork-safe...

ZZZ, я пока не знаю, что тебе ответить. Питоновская часть проста как пять копеек - и все сводится к libpg.
Я смотрел ее реализацию очень бегло - незнакомый код и все такое. Соединение закрывается на PGfinish - это верно.
Код этой функции шлет сообщение _внутренней_ системе - нужно закрыться. Что дальше - не смотрел, извини.

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

Ответ “в общем” - так же, как и C код, использующий libpq. Никаких неожиданностей или трюков, как я вижу.



Офлайн

#6 Июнь 12, 2010 00:40:53

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Fork-safe...

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

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



Офлайн

#7 Июнь 12, 2010 00:51:34

poltergeist
От:
Зарегистрирован: 2007-02-28
Сообщения: 522
Репутация: +  0  -
Профиль   Отправить e-mail  

Fork-safe...

ZZZ
в котором отрабатывается gc, который закрывает (в теории) соединение.
Не, в теории как раз соединением владеют два процесса, и оно закроется только тогда, когда оба процесса освободят этот ресурс.

З.Ы. Хотя мне не мешало бы освежить свои знания из области системного программирования, могу в чём-то ошибаться.



Офлайн

#8 Июнь 12, 2010 22:54:05

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Fork-safe...

poltergeist
Не, в теории как раз соединением владеют два процесса, и оно закроется только тогда, когда оба процесса освободят этот ресурс.
С точки зрения сокета да, но с точки зрения более высокоуровневой части… Я думаю, что в норме соединение должно закрываться не убийством сокета, а посыланием пакета (сообщения), говорящим о том, что соединение закончено. Вот тут и проблема. :-)



Офлайн

#9 Июнь 14, 2010 19:02:53

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Fork-safe...

ZZZ
С моей точки зрения не стоит надеятся, что fork нормально отработает на объектах сложнее файла, сокета или shared memory. Т.е. новое подключение в потомке - это идеологически правильно.
fork отлично работает с объектами уровня ядра. Если поверх этого объекта (а то и нескольких - канал связи, объекты синхронизации) что-то надстроили в user space - результат в общем случае непредсказуемый. Если только заранее не была предусмотрена эта возможность.



Офлайн

#10 Июнь 14, 2010 19:39:57

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Fork-safe...

Ну вот где-то так я и представил. :-)



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version