Pull to refresh
5
0
Антон Рябчевский @AntonRiab

SRE

Send message
Для окупаемости строительства такой инфраструктуры во вменяемые сроки нужна массовость в т.ч. со стороны международных компаний. А массовости — не будет. Т.к. небольшие и средние ЦОДы можно размещать недалеко от центральных офисов — о чем писали многие комментаторы выше.

Крупная же консолидация данных нуждается в благоприятной политической обстановке и иммунитету от произвола со стороны исполнительных органов. С чем в нашем государстве есть серьезные проблемы. Как по мнению коллег зарубежном(gitlab), так и российских специалистов — достаточно почитать комментарии к статьям про дело против nginx.
Да — в postgrest встроены фиксированные: сериализация данных, синтаксис фильтрации столбцов и даже джоины.
Нет — postgrest не реализует прикладной логики.
Более того, вы не будете делать верификацию связей таблиц с клиента, логирование и всего того, что эффективней провернуть на стороне, которая ближе к данным, т.е. в СУБД.

И как только вам понадобиться что-то более сложное, чем просто записать или вытащить данные по элементарному условию через предлагаемый OpenAPI, вы вспомните следующую строку статьи:
Также вы можете написать хранимую процедуру и, скорее всего, если вы будете использовать PostgREST, вы будете это делать.

И можете — это мягко сказано. В документации к OpenAPI Postgrest написано ещё конкретее:
It prevents arbitrary, potentially poorly constructed and slow client queries. It’s good for quality of service, but means database administrators must create custom views and stored procedures to provide richer endpoints. 

Как бы вам не хотелось, в данной архитектуре, вы очень быстро придёте всё к тем же хранимым процедурам, за исключением сериализации данных.
Год назад, я просвещал аудиторию идеологический близкой архитектурой на базе своего велосипеда — nginx модуль на bulk requests, статья Страх и ненависть в MiddleWare.
Основные категории прилетевших тапков были из следующих направлений:
  • ~50% — за регулярки — вы это обошли.
  • ~50% — это неприязнь разработчиков к подобным архитектурам с логикой в базе. Возможно это вы тоже обошли — отсутствием публикации в хабе «Разработка веб-сайтов».

Удачи в развитии!
На Github, watсh/star.
slim_middle_samples
  • Общая документация по технологии
  • Примеры реализации sql, nginx.conf
  • Методики тестирования
  • Туда же думаю неплохо было бы добавить примеры подключения FrontEnd

Модуль ngx_pgcopy, для отслеживания основного функционала. А так же для спецов по nginx, желающих поковыряться в кишках.
Получается, что всё же тестировать надо в связке?
Вначале, unit tests, потом всё в связке. Как и везде.

Классические middleware решения не открывают по соединению с базой на каждого клиента, а держат пул соединений и используют их по необходимости. Тысячи одновременно «открытых» stateless сессий с клиентами могут обслуживаться десятком соединений с базой. Есть подозрение, что 10_000 слонёнку не понравится, а уж 100_000 тем более.
Модуль еще в разработке, постоянно открытый пул еще не реализован — о чём и написано в конце статьи.

Одномоментно, на бд будет уходить столько, сколько настроите, остальные будут висеть в очереди nginx. Из-за ускорения логики, если её корректно перенести ввиде однопроходных запросов, серьезно сократится время обработки ответа. В результате — очередь пройдет быстрее, чем с толстым слоём.

А ещё у нас в какой-то момент времени может возникнуть необходимость во втором, третьем, N экземпляре nginx — и балансировки ради, и отказоустойчивости...10_000 100_000… лимитировать nginx чтобы libpq внутри не сожрал всю память

На той нагрузке о которой вы пишите — этой неизбежно и в классических решениях. libpq ест меньше, чем интерпретаторы fat middle.

Большинство проблем представленной технологии в её молодости — неполной реализации, отсутствию обширной обвязки с примерами и туториалами. Я этого и не скрывал.
Основная цель статьи — попытаться создать фундамент для будущего комьюнити. А не кидаться без оглядки с недоспелыми фруктами на амбразуру мирового продакшена.


Скорость скакуна, в начале 20ого века, как и несколько тысяч лет до этого, неспешным галопом составляла ~20км/ч, для дальних дистанции. Максималка более 60км/ч, при коротких пробежках в 1-3км. На лошадь можно посадить пару людей. А если пристегнуть повозку, то и с десяток.
В те времена считалось, что аппараты тяжелее воздуха летать — не могут. Однако, 1903 состоялся первый полёт такого аппарата. Скорости была ~15км/ч на дальность 260м. Он мог перемещать только одного человека.

Где сейчас самолёты, а где лошади — вы и сами знаете.
Если вы видели чью-то неудачную реализацию, то это не значит, что все такие. Вы же не будете считать труды Тьюринга и Эйнштейна ересью, только потому, что их кто-то может криво интерпретировать. И на толстом среднем слое можно написать так, что чёрт ногу сломит.
Еще важный момент, в разделе http нужно включить уровень логирования debug
error_log /var/log/nginx-error.log debug;
Раз уж вспомнили про nginScript… на сегодня, статьи на хабре и nginx.org не актуальны для текущей версии интерпретатора. Последняя версия из репов меркуриала не поддерживает какой-либо код внутри nginx.conf. Только js_include файла и ссылка на функцию через js_set. Актуальная документация на www.nginx.com
Кусок из http блока nginx.conf
js_include SourceJavaScript.js;
js_set $filter_by_njs FilterFunc;
$filter_by_njs используем в SQL запросе по аналогии с предыдущими примерами.

Файл SourceJavaScript.js с самым лайтовым вариантом регулярок:
function FilterFunc(req) {
    var v0, value, data, full_filter = "";

    var regex_value = /([\d\w]*)/;
    var regex_data  = /[\d\w,._]*/; 
    for (v0 in req.args) {
        value = regex_value.exec(v0);
        if (full_filter.length > 1) full_filter += " AND";

        if(value != 'undefined') {
            data = regex_data.exec(req.args[v0]);
            full_filter += " " + value[1] + "='" + data + "'";
        }
    }

    if (full_filter.length > 0)
        return " WHERE"+full_filter+"\n";
}


Из положительных моментов: гораздо меньше требований ко внешним библиотекам системы, в отличии от lua и perl. Если уже собирали nginx из slim_middle_samples через make likeiamlazy и есть меркуриал, то можно вытянуть последнею версию slim-a и собрать make njs.config, после чего должен завестись url типа http://127.0.0.1:8880/njs/simple_data/*?s_id=1. Полная версия js конфига в директории nginx.conf в slim_middle_samples.
Это был пример ускорения обработки. Ускорения логики вычисления — компьютерной логики!
Что касается бизнес-логики и аналитики, то уже при частичной автоматизации принятия решений — эти разделы будут очень тесно переплетаться.
Статья по тестам fat vs. slim middle запланирована, но очень не скоро.
Там же думаю будет и небольшой раздел по встроенной логики против внешней. Для специалистов по PostgreSQL исход по логике - очевиден.

Очень давно, на заре моего перехода на Postgres, я анализировал maillog. Файлики были ~100т. строк ~50MB. C помощью perl отформатировал в csv и отсеял всё лишнее, сколько длилась обработка — уже не вспомню. Далее попробовал вытащить из этих файлов обширную статистику с помощью perl. Вначале решил тестануть на одном файле и… выключил после более чем часа. Немного подумав решил, что с индексами дело пойдёт порасторопнее.

Первое решение реализовал по следующему пути: из perl c помощью insert загнал данные в базу и потом уже через select доставал нужные куски обратно в perl и доводил результат до конца. Загрузка в БД заняла ~30 минут, анализ ~20 минут.

Порывшись в документации СУБД наткнулся на COPY, загрузка сократилась до ~30 секунд! Вторым откровением были WITH Queries и рекурсивные запросы. В течении следующих нескольких дней я упорно оптимизировал запрос. Полностью избавился от loop во встроенной функции. Результатом был один SQL request размером с Эверест. И оно того стоило! Мне удалось оптимизировать анализ до ~10 секунд!
Почувствуйте разницу: ~50 минут и ~40 секунд, на загрузку и анализ, на одной и той же машине!

Т.к. в моих интересах защита предложенной мной технологии, подобный пример может быть скептично принят людьми, плотно не связанными с PostgreSQL. Поэтому хотелось бы услышать комментарии со стороны других специалистов по СУБД.

Что касается зарплат — для разработчика это не последний фактор. А актуальность для бизнеса — зависит от масштаба:
— Для стартапа, если вам нужно привлекать специалистов снаружи — это, возможно, дорогое решение.
— Если в основании стартапа специалисты по PostgreSQL, гуру perl старой закалки и профи по кишкам nginx — то это будет вам очень интересно.
— Огромная компания с высокой нагрузкой? Тут уже одной экономией на электричестве можно покрыть эту разницу зарплат.
На старте, все технологии относятся к академическим задачам.

Что касается логов, то есть подробный отладочный вывод в nginx-error.log. Если нужен лог только с модуля, то включить его можно раскоментировав строку в файле ngx_http_pgcopy_module.c.
//#define PGCOPY_DEBUG 1
Cегодня добавил автоматическое включение отладки, если configure, перед сборкой, был c опцией
--with-debug

Так же есть пример скрипта для парсинга лога модуля и отображения его в виде отформатированного xml с иерархией и порядком всех вызовов.
Опечатка, исправил, спасибо. На хабре принято их в скидывать личку, что бы не засорять комментарии.
Я больше позиционирую данную технологию как более универсальный вариант highload решения на базе nginx, чем существующие ранее в рамках nginx.

С Cache дело не имел, но порывшись по просторам сети нашёл следующую картинку на citforum
Архитектура Cache CSP


Статья старая, но посмотрев на гораздо более современные, думаю что картинка не утратила своей актуальности. И если это так, то архитектурно, связка Cache CSP + Cache СУБД очень близка к тому, что предлагаю я. Остальное уже дисциплина free open source vs. proprietary и postgresql vs. сache, что выходит за рамки данной статьи.
ngx_echo + парсиниг лога nginx. Покажите пример.
Т.е. вы предлагаете вместо своей странной middleware тестировать, как работает база? Спасибо, я вполне убеждён, что разработчики PostgreSQL уже всё, что нужно, протестировали.

Тестировать не субд, а собственную хранимую логику!
В этом есть суть и колоссальное преимущество над классическими решениями.
Средний слой настолько мал, что требует тестирование только фильтров.
А вся логика отлично проверяется не выходя из базы!

Итого
1. Тест регулярок для фильтрации, решение ngx_echo+curl+diff.
С ngx_echo я ошибся комментарием выше — он выводит клиенту. Этот момент исправлен в приведенном примере. В тесте ngx_pgcopy подключён, но можно закоментить pgcopy_query, а echo_after_body поменять на echo и будет standalone nginx.

2. Тест логики standalone sql.

Более подробное описание методов тестирования тянет на целую статью. В неизвестном будущем думаю запланировать это, а так же думаю добавить в модуль некоторый функционал для облегчения отладки фильтров.

map/for, if/case и восхитительные дыры
На страже дыр — регулярные выражения. Что эффективнее — я думаю, отдельный огромный и вечный вопрос. prepare/execute — в целевой архитектуре так же используется.
В вашем проекте HTML5-StarterKit для работы с базой используется DBIx::Class.
Код этого ORM содержит 113 регулярных выражений, без учёта динамических вызовов.

О производительности вы уже подумали? О масштабируемости? Сколько одновременно открытых соединений ваша архитектура выдержит? А сколько одновременных запросов? libpq и пользовательский контекст… Оно даже больше одного активного запроса на соединение не умеет.
Отнюдь! На каждое новое соединение клиента ngx_pgcopy открывает одно новое соединение с базой. Как и классические решения.
Максимальное кол-во запросов, соединений и пользователей зависит только от nginx и postgresql.
Я разделил MiddleWare на транспортную функцию и прикладную логику.
Транспортная функция ушла в ngx_pgcopy.
Прикладная логика отправилась в PostgreSQL.

По энергоемкости nginx+ngx_pgcopy = nginx+ngx_http_fastcgi_module(только модуль nginx, без толстого MiddleWare)
Прикладную логику и раньше можно было пихать в субд, но для транспорта приходилось подгружать объемный средний слой. Теперь это не обязательно.
+ Увеличение производительности при меньших ресурсах на обработку.
+ При использовании журналов и логов — логирование из коробки.

— Требует от разработчика некоторое переосмысление устоявшийся идеологии
— Нужно хорошее знание plpgsql и регулярный выражений

Про написание кода, тестирование и други моменты разработки — вопрос спорный.
Вам нужно развернуть вопрос. В идеале, с выдвижением вариантов. Т.к. на краткую версию вашего комментария напрашивается рекурсивный ответ: Потому что это эффективный путь к прямым потокам данных, о чем и идёт речь в статье.

Если вы об толстом Middle, то аргументация — в статье + смотрите ветку комментариев.
Ну а для FrontEnd разработчиков (JS в браузерах и иные потребители API через http), взаимодействующих с сервером, вся серверная сторона — BackEnd.
Вот скажем, вопрос сразу в лоб: а как вы юнит-тестирование организовывать будете?
  1. Perl. Потом, а можно и сразу ngx_echo + парсиниг лога nginx.
  2. Точка входа — таблицы, т.е. отлаживаются простыми инсертами. Пример positive/negative standalone sql теста на журналы и логи в slim_middle_samples.

Регулярные выражения нужны для составления и контроля. Какие альтернативы для динамических sql выражений и проверке не прямых данных на инъекции?
Если это делать в полуручном режиме(map/for, if/case на встроенные переменные), тогда без большого среднего слоя — никуда. Узкое место подобной реализации я описал в статье + увеличение объем кода.

PostgreSQL, который рассчитан на stateful клиентов
Парсинг и генерация json, xml, csv, чтотоеще — из коробки. Я думаю тут как раз обратное, но скорее всего, это уже дело религии.
В догонку, решение на Nginx-Perl
    perl_set $filter_by_perl "sub { 
        my $r = shift;
        $_ = $r->args;
        s/&/ AND /g;
        s/([a-zA-Z0-9_]+)=([a-zA-Z0-9_+-.,:]+)/$1='$2'/g;
        if(m/([a-zA-Z0-9_]+='[a-zA-Z0-9_+-.,:]+'(?: AND [a-zA-Z0-9_]+='[a-zA-Z0-9_+-.,:]+')*)/g) {
            return 'WHERE '.$1;
        }
        return '';
    }";

    server {
        listen       8880;
        server_name  127.0.0.1;

        pgcopy_server db_pub "host=127.0.0.1 dbname=testdb user=testuser password=123";
        location ~/perl/(?<table>[a-z0-9_]*)/(?<columns>\*|[a-zA-Z0-9,_]+) {
            add_header Content-Type "text/plain; charset=UTF-8";
            pgcopy_query GET db_pub 
                "COPY (select $columns FROM $table $filter_by_perl) TO STDOUT WITH DELIMITER ';';";
        }
    }

Еще некоторые особенности Nginx-Perl…
На content handler результат не предсказуем, т.к. неизвестно какой хэндлер раньше вызовется(ngx_pgcopy или Nginx-Perl). Соответственно, есть вероятность, что фильтр сработает после отправки sql запроса.
На access_handler не заработало, скорее всего дело в особенности ngx_pgcopy. Он вертится в этой фазе во время установки контакта с базой, принудительно откатывая статус соединения с клиентом. Что, вероятно не нравится Nginx-Perl и он преждевременно закрывает соединения.
Согласен с вами по поводу RPC. Добавление модификатора данных к самим данным, выводит на подобную реализацию.

RESTful, по основным требованиям, описанная в статье архитектура — проходит. Отличается только RESTful-pattern — отсутствие CRUD.

Stateless vs stateful. Вспоминаю момент, когда я начинал писать на plsql. Я часто использовал процедурные циклы внутри функций с соответствующими последствиями — проседанием скорости исполнения. Сейчас, я вкручиваю однопроходный декларативный sql в такие места, о которых раньше и не догадывался.
Думаю это больше вопрос привычки, как и использование stateless.

Если использовать запись всех изменений данных(журнал/лог), подозреваю, что можно получить что-то вроде multistateful — сохранение состояний клиента внутри разных многостадийных процессов.
1

Information

Rating
Does not participate
Location
Томск, Томская обл., Россия
Registered
Activity