Процедурное настоящее PHP

ПХП, к сожалению, в сердце ещё глубоко процедурный язык. Наткнулся на очередную особенность. Сначала небольшая предыстория.

Многие знают, что FastCGI в ПХП не настоящий — при каждом запросе весь код интерпретируется заново, это позволяет не заботиться об утечке памяти, сбросе состояния между запросами и ещё о многих вещах, а значит упрощает вообще всё, что соответствует духу ПХП — понизить до минимума порог вхождения. Конечно, это сказывается на производительности, но в некоторых случаях ПХП научился смягчать этот недостаток.

Например, есть такая вещь как «постоянные соединения» (persistent connections) — это специальные режим, в котором на уровне процесса PHP FastCGI соединения с чем-либо не закрываются и выдаются в следующий запуск кода уже открытыми. Чудесная вещь, ведь операция соединения, например, с «Ораклом» очень дорогая.

Конечно, кое-чем приходится расплачиваться — например, в открытом соединении «Оракла» остаются установленные предыдущим кодом параметры сессии, временные таблицы, незавершённые транзакции, наверное что-то ещё. Первые два пункта меня лично не пугают, так как параметры сессии у нас везде одинаковые и временные таблицы мы не используем, а вот последний — очень.

Как известно, ПХП может свалиться в фатальной ошибкой по многим причинам, одна из них — превышение времени обработки скрипта. Соответственно, скрипт может упасть и в середине транзакции, она останется незакрытой и может остаться висеть долгое время, пока соединение не получит скрипт, в котором встретится команда закрытия транзакции.

А пока этого не произошло, блокировки на базе могут привести к коллапсу.

В общем-то, есть хороший выход — у нас же есть сервис-объект для работы с базой? Вставим в деструктор принудительный откат транзакции (rollback) и все дела! Если транзакция была не завершена в силу фатальной ошибки, она откатится и всё! Ведь деструктор должен вызвааться на уничтожение объекта!

Как показывает практика, должен, но не обязан. Ниже простой код:

class ImportantToClose {
    public function __destruct()
    {
        // закрываем что-то важное
        echo "Вызвался деструктор\n";
    }
}

$itc = new ImportantToClose;

register_shutdown_function(function()  {
    echo "Вызвалась shutdown function\n";
});

set_time_limit(1);

for(;;);

Если запустить его в интерпретаторе и подождать секунду, то мы получим следующую ошибку:

PHP Fatal error: Maximum execution time of 1 second exceeded in fatal.php on line 18

Fatal error: Maximum execution time of 1 second exceeded in fatal.php on line 18 Вызвалась shutdown function

Можно ещё попробовать записать что-нибудь в файл, результат будет тот же — деструктор не вызовется, тогда как-то старая добрая процедурная shutdown function — да. Поэтому, если у вас есть нечто важное, что необходимо аккуратно закрывать, не делайте это в деструкторе, делайте в этой магической функции. В нашем случае, можно реализовать в каждом сервисе метод close и в этой функции вызвать этот метод для кажого открытого сервиса.

Кстати, не забудьте выставить внутри shutdown function нужное вам время выполнения, если предел будет превышен, то и эта функция будет прервана, в этом случае вам уже ничего не поможет закрыть ресурс корректно.

10 комментариев

Кухня блокадного Ленинграда

Читая в «Википедии» статью про Игоря Растеряева, увидел упоминание некой «хряпы» и «дуранды», никогда слов этих не слышал, заинтересовался. Оказывается, это упоминались названия со стола блокадников в Ленинграде.

Дуранда — это жмых от подсолнечного масла: плотные, желтовато-коричневые большие пластинки. За ней выстраивались огромные очереди, потому что ее давали без карточек. Просто так дуранду есть нельзя: ее нужно размачивать. Ее клали в большую кастрюлю с теплой водой, она там размякала, потом пропускали через мясорубку. Получалась тестообразная масса, из которой пекли лепешки. Это считалось деликатесом.

Хряпа — верхние капустные листья, которые остаются на поле после уборки кочанов. Обычно в пищу людьми не употребляются, используются как корм скоту. Чтобы отбить горечь, их вымачивают и засаливают.

5 комментариев