Метамоделирование: часть 2

Дописал клиентскую часть и оглянулся. Очень тяжелая отладка. Был бы MVC framework, как в PureScript, — было бы легче. Но тут он добавляет лишних телодвижений. Зачем, если есть модель на сервере. Хотел было уже взяться прикручивать N2O, как тут меня осенило.

  • Типы можно шарить без выпендрёжа между GHC (сервером) и Fay (клиентом).
  • Декодер уже заложен в самом Fay (Fay.Convert): конструкторы типов вместе с данными замечательно раскладываются в JSON (в соответствующие слоты). Подсмотрел даже у Сноймана пример реализации!
  • Запилил пакет для вебсокетов на Fay (опубликую на Hackage, когда моя страничка заработает).
  • Затащил поддержку различных протоколов.
  • Переписываю рендеринг на сервере, хочу посмотреть, что выйдет в итоге.

Получается эдакий FRP на коленке. Вообще, я очень доволен тем, что вырисовывается. Открытым вопросом пока является проброс новых событий для новых элементов страницы, которым ещё предстоит появиться. Либо дернуть на стороне Fay, либо прямо JS на сервере сгенерировать. Там видно будет. Пока не хочу забивать этим голову.

Метамоделирование: часть 1

Число таблиц в базе данных стало зашкаливать. Было принято жёсткое решение — не позволять их числу расти. И тут приходят на помощь метамодели. Есть всякие. 

  • Есть метаописание SOA с генератором запросов. В таком случае в каком-нибудь Oracle используют пакет dbms_sql, а в таблицах лежат нарезанными куски запросов или запросы с макросами.
  • Есть графы и гиперграфы, вырождающиеся в нечто подобное тому, что творится в Facebook.
  • Есть иерархические метамодельки, которые на первый взгляд дружат с ООП, но на деле порождают ад, когда попытаешься натянуть на них подсистему с Workflow внутри. 
  • И ещё что-то, чего я не видел, конечно.

Как водится, я в сторонке стоял по большей части, когда другие метамоделировали! Но некоторые вещи всё же умудрился пощупать вплотную. 

Взял иерархическую метамодельку за основу и решил натянуть её на Haskell с его интересными ADT. СУБД в проекте — PostgreSQL. Да, с текущими драйверами постгреса в хаскеле пока дела обстоят туговато. Все они завязаны на libpq, в недрах которого синхронно гоняются строки. 

Нативных драйверов готовых нет, есть два экспериментальных: 

  • Hasql >= 0.20.
  • postgresql-wire (нет на Hackage).

А они уже очень скоро понадобятся. Потому как уже очень скоро я упрусь в тормоза.

Завёл пока «строку», «число» и «время» в кач-ве типов атрибутов. Завёл парочку объектных типов. Завёл рута-родителя. И начал собирать грабельки.

Read more...Collapse )

10 KLoC: achievement unlocked

Хобби-проект, стартовавший как сайтик-эксперимент, постепенно превратился в распределенную систему. Состав примерно такой...

Окружения 

  • лаптопа (DEV);
  • двух серверов (STAGING, PRODUCTION).

Компоненты

  • собственно, application server;
  • бот, работающий в режиме интеграции (см. сериалы «Web Scraper», «Парсер»);
  • телеграм-бот, про который я расскажу чуть ниже;
  • билд-деплой-тула, умеющая в матрицу «компонент» × «окружений» и билдить, чистить, деплоить, перезапускать.

Телеграм-бот

Бот работает по двум направлениям: 

  • шлёт админам ресурса баг-репорты (5XX коды, + некоторые критические уведомления (самодельная наколеночная Capacity management system: alarms, thresholds, metrics);
  • шлёт менеджерам уведомления о человеческих заявках.

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

Благодаря Servant, API прекрасно шарится без лишних движений между сайтом (клиент) и ботом (сервер).

А 10 KLoC — блин, это всё трехэтажные SQL, и hamlet (html) шаблоны, опердень ведь должна быть динамической, не так ли?

Web-scraper: часть 8 и последняя

Забыл сказать, что описанное в прошлом посте, реализовано и просто работает. 

  • Локальный клиент по расписанию собирает дельту и шлёт на сервер. 
  • Сервер вычитывает очередную пачку данных и кладёт их в табличку базы. 
  • Локальный клиент умеет возобновлять свою работу с места падения/остановки, хотя этим мало кого сейчас удивишь.
  • С фантомом on-demand интеграция работала 8 секунд, с servant-client — 2 секунды, сейчас данные достаются из кэша за менее, чем секунду. И это успех.
  • Обновил весь проект до GHC 8.4.3. Побочный эффект апгрейда: Снойман сделал меня мейнтейнером пакета yesod-fay, которым помимо меня пользуется ещё полтора человека. И это тоже успех, хоть и сомнительный.

Надо сформировать новый сериал. О работе пока не буду писать.

Web Scraper 7 + Job change: servant-client in production

Yesod + Servant два в одном. Меня немного мутит от факта стыковки их вместе, но пока пусть комбайн живёт. 

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

Теперь шаг 2 — отвязать интеграцию от сервера и добить offline tool для работы в режиме джобы, и начать собирать «статистику» на регулярной основе. Путь долгий, но надо двигаться дальше.

Сегодня был первый день на новом рабочем месте. С непривычки немного понервничал, но в какой-то момент успокоился и получилось продвинуться в освоении проекта. 

Теперь работаю в «коворкинге». На место офиса пришёл офис поменьше с чужими людьми, сдающими посадочное место в аренду. Условия спартанские: стол, стул, розетка. Отдельно — чайник, чашка. 

Из неприятного: пока что люди 50/50 ок и не ок на уровне интуиции. Вечером мужик из другой комнаты на кого-то орал. Другой мужик докопался до меня среди дня с позиции заискивания передо мной. Женщина в комнате со мной ОЧЕНЬ громко говорила по телефону один раз. Извинялась потом. Что ж, если так — надо обозначать границы дозволенного и пресекать вторжения. Доверия нет таким людям, поэтому сумка с лаптопом идёт со мной на обед за пределы сего «коворкинга». Месяц уплачен. В сентябре попробую другое место.

Web Scraper 6: yet another client

В общем, теперь есть ещё один клиент. Замечательно. Его оказалось даже легче интегрировать с основной системой, чем я думал с самого начала. Дольше собирался. 

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

  • wreq
  • phantomjs
  • webdriver
  • servant-client

Нормально насобирал за всё это время. Прикрутить MAS, distributed computations к этому делу, и можно заниматься ботоводством в промышленных масштабах. 

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

Web Scraper 5: performance

Вообще webdriver + selenium в связке очень хорошо себя показывают, но offline и долго. К примеру опрос одного таргета занимает 161 минуту и высасывает 2 ГБ трафика, из которого мне надо лишь 2 МБ (максимум, на самом деле ещё меньше). 

Сие очень опечалило настолько, что поставил приложеньку на смартфон, воткнул mitmproxy и прослушал API, удалось восстановить, но есть нюансы. Например, ограничение на 10 тысяч запросов в день. Мне надо больше. Надо запиливать ещё одного клиента, на сей раз под API. 

Собрал таким образом (через селениум) только два таргета (всего около сотни). Добавил сравнение с предыдущим анализом, ну чтобы не делать лишнюю работу в случае расхождений. Думаю, всё же клиент существенно упростит мне задачу, и я смогу собирать более 1 таргета за ночь, а скажем 2 или 3.

Динамический ввод капчи — отдельная вещь. Осталось лишь пульнуть уведомление на смартфон, чтобы не киснуть возле компа всё время.

Web Scraper 4: chromium server-side

Опробовал затащить всю связку agent + selenium-standalone + chromium-driver + headless chromium на сервер.

  • Тесты поначалу были оптимистичными. Словил капчу.
  • Научился обмениваться User-Agent и кукисами между разными браузерами (chromium и w3m). 
  • Научился распознавать и вводить капчу через w3m.
  • Но не только я учился. Целевой ресурс хорошо стал работать с ботами и browser foot-print-ми.
  • Раз уж «умею» из консоли читать картинки, w3m становится более не нужен. Запиливаю интерактивный режим ввода капчи.
  • GDPR внедряют. Причем вроде скрипт-то простой, который ставит куки и рефрешит страницу, а вешает намертво безголовый хром. Блокер. Потому что с сохранением кук проблем нет вообще. А с загрузкой.. В общем, обойти GDPR заглушку я не могу уже не первый день.
  • Напоследок, хромиум стал падать. Всё сводится к тому, что недостаточно памяти на сервере. Приехали. Переезжать на более мощное окружение из-за Google? Нет!
  • В довершении всего выхватываю OOM на сервере.

Ещё одна гипотеза — мимо. Берусь за следующую. Там же целая бездна вариантов. Какой-то да должен выстрелить.


Web scraper 3: PhantomJS last chance

Собрал из исходников PhantomJS. 

  • Версия 2.1.1 не собирается. В зависимостях Xcode.
  • Текущий релиз Xcode не совместим с системой. Предлагается апгрейд. Xcode 9.2 взлетает.
  • Установка тащит с собой qtbase 5.5. И падает на этой баге. Сделал бэкпорт коммита для 5.6 в 5.5. PhantomJS собран.
  • Запускаю phantomjs в режиме --webdriver=8910 --webdriver-selenium-grid-hub=127.0.0.1:4444.  Падает с ошибкой 
ghostdriver - main.fail
  • Делаю фикс hub_register.js. Он заводится.
  • Собираю тестовый пример и пускаю через Selenium standalone 3.8.1. Вместо фантома выбирается хром! В селениуме поддержку фантома торжественно отключили! 
  • https://github.com/SeleniumHQ/selenium/issues/5295 
  • https://github.com/vvo/selenium-standalone/issues/376
  • Откатываюсь до 3.7.1, пускаю тестовый пример.
  • Фантом стартует на левом порту, вебдрайвер слушает всегда порт 8910, даже если указать другой. Селениум ломится на левый порт фантома вместо порта вебдрайвера.
  • Пересборка ветки bleeding-edge, близкой к версии 2.5, падает на mongoose: 
Undefined symbols for architecture x86_64: "_mg_printf", "_mg_read", "_mg_start", "_mg_stop", "_mg_write"

Очевидно, эта ветвь тупиковая.

Парсер третий: Selenium

Selenium — это чисто точка входа в удивительный мир возможностей использовать разные драйверы. 

Хром и мозилла завелись сразу. 

PhantomJS ругнулся на ghostdriver, зашитый в его кишки. В кач-ве решения предлагают пересобрать его из исходников, захачив ghostdriver.

HtmlUnit валится с ошибкой, вах, не могу поменять read-only поле constructor, исполняя что-то реактоподобное, что висит на целевом ресурсе.

Тут я на распутьи: либо таки тащить chrome или chromium на сервер, либо возиться и пересобирать phantomjs под OS X и Linux.