Alex Efros @powerman
Software Architect, Team Lead, Lead Go Developer
Information
- Rating
- 1,970-th
- Location
- Харьков, Харьковская обл., Украина
- Date of birth
- Registered
- Activity
Specialization
Backend Developer, Software Architect
Lead
From 10,000 $
Designing application architecture
Golang
Linux
Docker
Network security
Modular testing
Mentoring
Development of tech specifications
Software development
High-loaded systems
Такие понятия передавать эффективнее всего за чашкой чая, чтобы можно было сразу отслеживать и корректировать неправильное понимание.
Можем продолжить эту беседу в аське, если захотите.
Я вот программист. И меня максимизация прибыли не интересует. Меня интересует код. Меня интересует разработка нужных, "живых" систем, которыми пользуются, которые решают задачи пользователя, и которые не раздражают пользователя глюками. И от софта, которым я сам пользуюсь, я жду того же: чтобы он решал мои задачи и не глючил.
Для достижения этого эффекта переодически приходится писать с нуля библиотеки, а не использовать готовые (просто потому, что после взгляда в код этих "готовых" начинает тошнить, и ты понимаешь, что код написанный на базе этих библиотек надёжно работать не будет никогда в принципе). Переодически приходится изобретать велосипеды, заточенные под конкретные задачи, вместо использования готовых универсальных решений. Изредка попадается готовое, и при этом достаточно качественное решение - тогда радость моя просто неописуема, т.к. можно немного полениться, поюзать готовое, и получить результат быстрее. Чаще бывает иначе - просто нет никакой возможности переписать всё что нужно, и приходится вынужденно пользоваться тем, что есть.
P.S. Существует мания, когда всё подряд переписывается без веской причины - это имеет смысл только в рамках обучения. Но есть и другая ситуация, когда ты реально можешь написать библиотеку качественнее готовой, причём патчить готовую сложнее написания своей (из-за архитектурных проблем, например), причём в текущем проекте требуется уровен качества выше того, которые предоставляет готовая библиотека - тогда написание своей имеет смысл, хотя и часто не делается из-за экономических причин (повышение качества на X и увеличение времени разработки на Y не оправдывает увеличения расходов на Z). Но этот экономический расчёт зачастую верен только в краткосрочной перспективе, а в долгосрочной это переписывание всё-равно бы окупилось.
Во-первых, как я написал выше, "overquality не нужно нашим заказчикам", по крайней мере абсолютному большинству (бывают и исключения - мой заказчик платит мне уже больше 5-ти лет как раз за то, чтобы я писал насколько качественно, насколько считаю необходимым - но он, и до того как меня нашёл, и после этого, успел поработать с другими программистами, и, в результате, остался со мной - вероятно потому, что мой код таки работает так, как ему нужно).
Во-вторых, лично я не просто готов платить больше за качественный софт, вопрос стоит иначе: я не готов платить ни копейки за некачественный софт. Что касается цен M$, то, как известно, они зарабатывают ГОРАЗДО больше, чем тратят на разработку. Так что, я уверен, они имеют возможность писать гораздо более качественно, сохраняя прежний уровень цен. Просто им это не нужно.
P.S. А по поводу изобретательства велосипедов, я уже когда-то цитировал эту фразу на хабре, но я повторюсь, уж больно хорошо сказано автором JSON:
Я думаю, что этот совет можно применять так, чтобы это не усложняло поддержку кода. Во-первых, можно привыкнуть писать/читать более простой, хоть и не такой краткий код. Тогда удобно будет читать именно его. :) Во-вторых, краткость здесь нужна ради удержания всего проекта в голове, а не ради того, чтобы сложную функцию "запинать" в 3 строки. Поэтому если при нормальной записи кода проект в голове перестаёт умещаться, нужно не код более кратко записывать, а проект дробить на изолированные кусочки, которые не нужно одновременно удерживать в голове.
Иными словами, задачи организаций, и задачи описанных в статье программистов, очень сильно расходятся!
А авторы вышестоящих комментариев, к сожалению, этого не понимают. И поэтому пытаются спорить. Для организаций описанные в статье "способы решения этой проблемы" категорически не подходят (и автор статьи как раз это отлично понимает). Не походят не потому, что проблемы с въезжанием в код у них нет - она есть - а потому, что эта проблема для организации совсем не так важна, как многие другие проблемы - например, проблема взаимозаменяемости людей, проблема сдачи проекта в срок, проблема обеспечения необходимого уровня продаж, проблема минимизации расходов на поддержку проекта, etc. И следование описанным в статье рекомендациям просто не выгодно, ведь задача большинства организаций - "заработать денег", а не "написать максимально качественный код любой ценой".
А вот для описанного в статье программиста, задача стоит "сделать его идеально". И, довольно часто, в определение "идеальности", у программиста не входят такие вещи, как сдача проекта в срок, или облегчение дальнейшей поддержки проекта. Чаще эта "идеальность" относится к внутренней реализации, к элегантности и эффективности кода. А для написания такого кода, и требуется "удерживать его целиком в голове"! Вот для этой ситуации всё описанное в статье абсолютно верно.
Определитесь, в чём Ваша цель, и Вам станет понятно, подходят Вам эти рекомендации, или нет. :)
Теперь Датское королевство переписано надлежащим образом, без перехватов die и вызовов exit. Всё стало очень культурно и аккуратно. И проблема с goto отпала сама собой - сейчас фреймвок сам генерит исключение вместо exit. И никаких вложенных eval-ов в тех местах, где может быть редирект у меня ни в одном проекте нет... и если когда-нить появятся, значит после этого eval необработанные исключения будут передаваться наверх, как и положено.
В общем, ощущения у нас были правильные - изменил архитектуру и проблема исчезла.
К сожалению, я не знаю в чём конкретно заключается это "завершение работы и освобождение ресурсов", может он к моменту вызова блока END ещё ничего такого сделать не успевает.
Что касается утечек - они 100% должны быть, ведь после обработки каждого запроса в память добавляется ещё один блок END, при этом старые блоки из памяти не освобождаются. Другое дело, что много он памяти врядли занимает, и рестарт процесса раз в несколько тысяч запросов эту утечку памяти решит.
perl -e '
BEGIN {
*CORE::GLOBAL::exit = sub {
goto FASTCGI_NEXT_REQUEST;
};
}
while (1) {
eval { that_cgi_script() };
FASTCGI_NEXT_REQUEST:
last;
}
sub that_cgi_script {
local $SIG{__DIE__} = sub { print "<p>error: $_[0]"; exit; print "XXX\n" };
print "before buggy code\n";
eval { buggy_code() };
print "after buggy code\n";
}
sub buggy_code {
die "error!";
print "after die\n";
}
'
Этот пример выводит:
before buggy code
<p>error: error! at -e line 20.
after buggy code
т.е. goto не срабатывает как надо, и выполнение продолжается после eval. Фактически exit в обработчике $SIG{__DIE__} срабатывает как return - ведь "print XXX" не срабатывает.
Я нашел как этот баг обойти. Нужно в блоке BEGIN добавить no-op обработчик CORE::GLOBAL::die (который просто симулирует работу системного):
*CORE::GLOBAL::die = sub {
if ($SIG{__DIE__}) {
my $s = $_[0];
$s .= sprintf " at %s line %d.\n", (caller)[1,2] if $s !~ /\n\z/;
$SIG{__DIE__}->($s);
}
};
и теперь этот тест работает корректно, и выводит:
before buggy code
<p>error: error! at -e line 27.
Впрочем, объяснить почему добавление такого обработчика заставляет goto работать правильно я пока не готов. :( Возможно, дело в том, что мой обрабочик всё-таки не до конца симулирует стандартный обработчик перла. Правда, на множестве разнообразных тестов, которые я прогнал - я разницы между ними не заметил, если не считать вышеописанную.
Что касается пути самурая - честно говоря, качество большинства модулей на CPAN настолько низкое, что путь самурая в этих условиях - пользоваться этими модулями, вместо того чтобы сделать простое решение заточенное под Вашу задачу. Да и отлаживать баги в своём коде значительно проще, чем в цепочке CPAN-модулей.
Что касается Apache::Request и тяжёлого CGI.pm. Да, в принципе я согласен. Но, а Вам не всё равно, насколько тяжёл CGI.pm, в условиях FastCGI? Параметры он возвращает? Аплоады контролирует? Что ещё от него нужно? Не html же через него выводить... :)
Лично для меня, основной довод в пользу именно FastCGI, это безопасность: CGIшки запускаются от пользовательского аккаунта, внутри веб-сервера они не выполняются, никаких сложных механизмов обеспечения безопасности типа SuExec и CgiWrap не используется. Второй довод - поддержка разных веб-серверов. Мы давно хотели отказаться от апача, и использование FastCGI облегчает попытку попробовать другой веб-сервер.
Кстати, на CPAN появился модуль FCGI::Spawn, который использует FCGI как раз для обеспечения безопасности: он не держит в памяти конкретную CGI, а, вместо этого, при каждом запросе подгружает и запускает CGI, указанную параметром $ENV{SCRIPT_FILENAME}. Как там написано: "hot ready for hosting providing".
Я не совсем понял, что Вы имели в виду под "только применительно к Perl". Все эти особенности FastCGI к Perl применимы так же, как и к другим языкам. Ничего Perl-специфичного в технологии/протоколе FastCGI нет.
Если это не под NDA, расскажите, пожалуйста, чего именно Вы натерпелись при использовании Perl под FastCGI, и какие библиотеки при этом использовали.