Как стать автором
Обновить

Комментарии 18

sleep в скрипте — маскируемый race conditions. Так делать не надо. Что произойдёт, если gdb по какой-то причине потратит на чтение 3.1с?
Хорошее замечание. Я так и не нашел способа как делать надо.
Я давал сильную искусственную нагрузку на тестовый баунсер и измерял время. Больше секунды не получалось на моих серверах.
Было бы интересно узнать как делать правильней…
Видимо, дело в gdb.

Хотя если бы эту задачу решал я, раз уж мы говорим про патчинг кода (а вызов функции в контексте gdb — это такой извращённый метод патчинга реального времени), то бы просто добавил в код postgre обработчик сигнала (любого неиспользуемого). По вызову сигнала дёргается функция, после чего выполнение продолжается.

После этого процесс передёргивания больше не будет ставить pgbounce раком на 3 секунды с шансом огрести race.
Задача была «не перезапускать сервис».
Была идея написать программку, которая коннектится с помощью ptrace() и дёргает эту функцию. Но gdb ведь делает всё красиво: call он вызывает в неком dummy-frame, а не в текущем. Я бы такое не осилил закодить. :)
Если «не перезапускать любой ценой» — это важно, то в решении проблемы. Все сервера когда-то виснут.

А так — пропатчил, да дёргай себе на удовольствие сигналом перечитывание.
Второй абзац статьи же про это. По HUP pgbouncer перечитывает свой конфиг, но не делает res_init() (перечитать resolv.conf).
Мне кажется, что неплохо бы все-таки сначала делать break на что-нибудь безобидное, и только потом call. А то кто его знает, из какой странной позиции вызовется res_init(), и не испортит ли его вызов текущий контекст.

Но способ, конечно, довольно дикий, браво :-)
А вы правы :).
Наверное, правильней делать так:
при подключении gdb'ой нужно смотреть в каком контексте мы остановились (gdb это покажет последней строкой вывода), и если мы остановились на getaddrinfo(), то лучше отключиться от процесса и попытаться заново.
Кстати, забыл написать: вызов res_info() дёргает getaddrinfo() даже если gdb остановился в контексте getaddrinfo() и всё после этого нормально работает. :)
Я так понял, call завершает выполнение текущего контекста, после чего выходит в dummy-frame (контекст) и дёргает вызванную функцию.
В результате, ничего не ломается.

Осталось только понять как gdb останавливает программу при простом подключении, когда мы еще не поставили точку останова. Где он останавливает программу? В начале функции как при brake или где угодно?
А не лучше ли будет использовать nscd? Он делает тот же самый res_init(), а getaddrinfo() будет обращаться к nscd. После правки resolv.conf чистим кеш и делаем nscd reload. Правда, установка nscd во FreeBSD требуется отдельно, насколько я понимаю.
Отличный комментарий.
Но мы тут подумали и решили, что в нашем случае прописывать «cache» для hosts в /etc/nsswitch.conf было бы не очень хорошо, так как это может не очень хорошо повлиять на работу некоторых наших сервисов на машине с pgbouncer. Но идея отличная.
nscd работает для gethostbyname, но не работает для getaddrinfo.
Я проверял на centos x86_64. Точно работает для getaddrinfo. Посмотрите код функции в glibc. Там есть макрос USE_NSCD.
FreeBSD, man nsswitch.conf:
hosts getaddrinfo(3), gethostbyaddr(3), gethostbyaddr_r(3),
gethostbyname(3), gethostbyname2(3), gethostbyname_r(3),
getipnodebyaddr(3), getipnodebyname(3)


Если в nsswitch.conf прописать 'cache' дял hosts, то будет работать.
Дело в том, что nscd кэширует хосты так, что лучше бы не кэшировал.
Гораздо лучше поставить предсказуемый кэширующий named.
Возможно, я неправильно понял, но этот способ работает только если целевая программа собрана с отладочной информацией, что далеко не всегда так.
Не-не, gdb знает сигнатуры функций из стандартной библиотеки (libc).
Я же делал это на серверах, которые были собраны без отладки: libc, pgbouncer, ядро системы.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.