Обнаружил баг в Apache/PHP.
Проявляется в том, что некоторые запросы переходят в keepalive state (т.е. ждут продолжения запроса после окончания предыдущего на том же сокете) и остаются там навечно. Таким образом, постепенно все процессы Apache попадают в это состояние, если keealive включен, а так как выйти из него у них не получается - сервер постепенно задыхается. Причём таймаут, который у апача есть для избежания таких ситуаций, не работает. Собственно, с этого всё и началось - сервер наш начал вдруг необьяснимо дохнуть, притом что ни нагрузки процессора, ни наплыва посетителей особого не наблюдалось.
По расследованию оказалось, что функция, которая должна работать по таймауту, не вызывается. А не вызывается она потому, что есть такой флаг alarms_blocked, который блокирует обработку сигналов. Когда обработка сигналов неуместна, вызывается ap_block_alarms(), которая делает ++alarms_blocked, а когда снова можно - ap_unblock_alarms(), в которой написано вот что:
--alarms_blocked;
if (alarms_blocked == 0) {
и далее обрабатываются случаи, видимо, когда в период, что сигналы были заблокированы, был сигнал и теперь пора его отработать.
Постфактум я думаю, что уже увидя этот код, я должен был заподозрить неладное. Но с первого раза я не понял, в чём дело. Запустил отладчик и посмотрел значение alarms_blocked в зависших процессах. И оно оказалось - -1, -2 и т.п. Тут до меня дошло - а что будет, если какой-то кривой код вызовет ap_unblock_alarms(), не вызывая перед этим block()? А то и будет, что с этого момента не судьба этому процессу больше принимать сигналы по таймауту - то есть судьба, но ровно тогда, когда кто-нибудь ему это _запретит_.
Завтра буду искать, кто же это такой умный этот флаг портит. То ли это в самом Apache, то ли в модуле PHP - он тоже с этими функциями играется.
Проявляется в том, что некоторые запросы переходят в keepalive state (т.е. ждут продолжения запроса после окончания предыдущего на том же сокете) и остаются там навечно. Таким образом, постепенно все процессы Apache попадают в это состояние, если keealive включен, а так как выйти из него у них не получается - сервер постепенно задыхается. Причём таймаут, который у апача есть для избежания таких ситуаций, не работает. Собственно, с этого всё и началось - сервер наш начал вдруг необьяснимо дохнуть, притом что ни нагрузки процессора, ни наплыва посетителей особого не наблюдалось.
По расследованию оказалось, что функция, которая должна работать по таймауту, не вызывается. А не вызывается она потому, что есть такой флаг alarms_blocked, который блокирует обработку сигналов. Когда обработка сигналов неуместна, вызывается ap_block_alarms(), которая делает ++alarms_blocked, а когда снова можно - ap_unblock_alarms(), в которой написано вот что:
--alarms_blocked;
if (alarms_blocked == 0) {
и далее обрабатываются случаи, видимо, когда в период, что сигналы были заблокированы, был сигнал и теперь пора его отработать.
Постфактум я думаю, что уже увидя этот код, я должен был заподозрить неладное. Но с первого раза я не понял, в чём дело. Запустил отладчик и посмотрел значение alarms_blocked в зависших процессах. И оно оказалось - -1, -2 и т.п. Тут до меня дошло - а что будет, если какой-то кривой код вызовет ap_unblock_alarms(), не вызывая перед этим block()? А то и будет, что с этого момента не судьба этому процессу больше принимать сигналы по таймауту - то есть судьба, но ровно тогда, когда кто-нибудь ему это _запретит_.
Завтра буду искать, кто же это такой умный этот флаг портит. То ли это в самом Apache, то ли в модуле PHP - он тоже с этими функциями играется.
Re: Slackers!
no subject