вторник, 23 июня 2020 г.

Мелочь, а приятно

      Давным давно я писал генератор swagger спецификации по аннотациям - https://github.com/yvasiyarov/swagger. То есть ты пишешь API, а перед хендлером в комментарии пишешь аннотацию этого метода. Потом вызываешь генератор и он автоматом генерирует swagger спецификацию API и поднимает рядом веб клиента. 
                   Потом я его забросил, так как появились более продвинутые реализации. И тут недавно мне снова понадобился swagger генератор и я начал смотреть что же есть из актуального. Самым продвинутым на текущий момент является https://github.com/go-swagger/go-swagger. Каково же было мое удивление когда в его документации я обнаружил: 
      
The toolkit has a command that will let you generate a swagger spec document from your code. The command integrates with go doc comments, and makes use of structs when it needs to know of types.
Based on the work from https://github.com/yvasiyarov/swagger.
В общем мелочь, но приятно когда не забывают сказать спасибо :-)

четверг, 28 мая 2020 г.

https://regex101.com/

      Хочется сказать огромное спасибо ребятам которые сделали https://regex101.com/. Я думаю трудно даже представить сколько труда было вбухано в создание этого инструмента. Но получился реально классный инструмент для отладки регулярок. Он мне тонну времени с экономил. 

PHP: Асинхронность и производительность

     Никогда не думал что буду когда-нибудь снова писать про PHP. Но Жизнь напоминает что никогда нельзя говорить никогда. Итак: последния веяния программистской моды докатились с 5-ти летним опозданием до PHP. Лучше поздно чем никогда, заметит PHP программист и с этим трудно не согласится. 
      Если погуглить "promises in php" то реализация https://github.com/guzzle/promises окажется на 3-ем месте по популярности. Ничего удивительного что одноименный HTTP клиент вызывает у php-коммьюнити дикий восторг. Асинхронный, на промисах... и похеру что внутри сидит все тот же curl_multi_exec(если быть совсем честным не только он. есть еще варианты fall-back-а в случае недоступности curl).  В общем с точки зрения архитектуры GuzzleHTTP и в правду не плох. Но вот производительность он убивает напрочь. Я заменил в одном из нагруженных хенджлеров GuzzleHTTP на примитивный синхронный HTTP клиент(тоже curl based) и получил прирост примерно в 2 раза меньшее время ответа. Реально вместо 1000-1200ms стало 500-600ms. Отсюда вывод - не надо усложнять. Если вам нужно сделать всего 1-2 HTTP запроса - вам не нужна асинхронность, промисы и прочие красивые штуки. И если вам важно время ответа - то асинхронность вам скорее всего тоже не нужна. Асинхронность - это оптимизация пропускной способности, возможность одновременно сделать 20 запросов. А если вам нужно только 2 - сделайте их последовательно и будет сильно быстрее(как это не парадоксально).
      И вообще кажется я становлюсь ретроградом. Но это пока не точно.  Возможно просто новые технологии гавно. 

понедельник, 25 мая 2020 г.

Философии пост

        На меня редко такое находит, но сегодня решил по-философствовать. Как водится на злобу дня - covid и все вот это. Когда все это начиналось, я думал - надо на пару недель зажмурится - и весь этот херовый сон пройдет, и все будет все как прежде. Как в детстве школу закрывали на пару недель из-за эпидемии гриппа, так и сейчас будет. Сейчас я склонен думать что не пройдет, и что этот covid останется в наших головах на всегда. Скорее всего не в виде конкретного заболевания, а в виде паранойи. Такой пост-травматический синдром ковидника, и опытного сидельца дома. Вообще меня не покаидает ощущение что мы сейчас живем в эпоху каких-то глобальных перемен, что вся заварушка только начинается.
            Интересно как по разному люди реагируют на эту ситуацию. Кто-то запасается всем в прок и побольше. Начиная с туалетной бумаги и заканчивая лимоном. Кто-то перечитал столько информации по разным вирусам что им впору на медфак поступать, причем сразу на второй курс. Кто-то стал параноить и безжалостно осуждать "идиотов" выходящих на улицу без костюма хим защиты и противогаза. Кто-то наоборот насрал на все и продолжает бухать. 
              Ну а я купил себе дом и теперь развлекаюсь с этим конструктором для больших мальчиков не доигравших в детстве в Lego. Среди моих развлечений, конопатка, сборка бензопилы, приведение в чувство бензотриммера неопознанного китайского производителя у которого половина комплектующих включая инструкцию куда-то просрали. Из плюсов - просыпаться под пение птиц по утрам и свежайший воздух. В общем весь этот covid мне начинает нравится. Интернет бы только провести в мою избушку - вообще цены ей не будет :-)   

вторник, 31 марта 2020 г.

Теперь еще и немного IOS разработчик

        Я не люблю зависеть от других людей, особенно в плане понимания/реализации своих идей. Мне проще самому залесть в код, почитать его и понять что происходит в системе и принять решение о том что и как нужно делать. А иногда и закодить все самому. Примерно так я стал немного IOS разработчиком. 
          Что хочется сказать - после простоты Golang конечно чувствуется перегруженность Swift. Огромное количество синтаксических конструкций, огромное количество правил их применения. Такое ощущение что хотели всем угодить, чтобы у всех все было. В общем я считаю что надо стараться упрощать вещи, а не усложнять их. Также бросается в глаза что многое похоже на Rust. Не знаю кто там на кого влиял, но определенная степень влияния имеет место быть.
           

среда, 11 марта 2020 г.

Vector и Rust

      В последнее время к сожалению не так много нового удается узнать, из того что бы зацепило.  Все основном приходиться get shit done заниматься, не вдаваясь в подробности. Но недавно наткнулся на коллектор логов под названием Vector (vector.dev). Запустил в проде, посмотрел сколько он памяти/cpu ест и удивился. Там что-то вроде 60mb всего. Решил заглянуть внутрь - а там Rust. Тут то я и проникся к нему уважением. Ибо при такой нагрузке то же самое на Golang кушало бы заметно больше ресурсов. Тоже filebeat написаный на Golang - оказалось тем еще говном, слабо пригодным к использованию в нагруженном проде. В ozon его переписали и заменили своей поделкой, которую к сожалению никто не торопиться выкладывать в open source. 
           Дальше я принялся разглядывать Rust. Его девиз - zero cost abstraction. Где-то я уже видел подумал я. А точно, то же самое старина Bjarne Stroustrup вещал:

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

среда, 12 февраля 2020 г.

Node.js event loop monitoring

       Практически в каждой статье по  Node.js и о том как правильно разрабатывать бэкэнд на JavaScript есть пространные рекомендации на тему event loop. О том что не надо занимать event loop на долго иначе другие запросы не будут обрабатываться и прочие прелести однопоточного программирования. Но при этом нигде не сказано как именно мониторить выполнение этих рекомендаций.  Даже такие простые вещи как event loop delay зачастую измерялись не правильно.  За примерами далеко ходить не надо. В prom-client - официальной библиотеки для мониторинга node.js приложений через prometheus event loop delay мониторился через вызов setImmediate() несколько раз в минуту.  
       Очевидно что event loop delay это характеристика которая меняется гораздо чаще. Мерить ее несколько раз в минуту это все равно что вычислять среднегодовую температуру по 3 измерениям в году. В общем эта метрика показывала только самые грубые задержки, и была в общем бестолкова. 
       На самом деле в недрах perf_hooks была спрятана возможность для гораздо более точного измерения event loop delay - perf_hooks.monitorEventLoopDelay() Через эту функцию можно запустить измерение event loop delay на уровне libuv, и делать это не раз в сколько-то времени а на каждом обороте event_loop. Собранные данные кладутся в гистограмму, поэтому можно увидеть не только мгновенное значение, которое большой ценности не представляет, но и min/max + перцентили. 
        Сегодня мой очередной пул реквест ждавший своего часа долгие пол года слили в мастер - https://github.com/siimon/prom-client/pull/278 Так что скоро эти метрики будут доступны всем.  Для более полного понимания того как работает event loop и какие функции на каком этапе event loop вызываются я всячески рекомендую прочитать документацию libuv - https://github.com/libuv/libuv/blob/v1.x/docs/src/design.rst На мой взгляд она сильно более толковая чем официальная документация Node.js - https://nodejs.org/uk/docs/guides/event-loop-timers-and-nexttick/

вторник, 11 февраля 2020 г.

Hierbas de Mallorca

        Я, как наверное и многие другие, люблю во время поездок накупить всякой дичи, и потом по приезду пробовать. Как правило добрая половина всего этого добра потом отправляется в утиль. Но иногда бывают исключения. На этот раз меня приятно удивил Hierbas de Mallorca
По названию не трудно догадаться что привезен он с Майорки, и сделан на травушках-муравушках. Я ожидал что это будет обычный местный самогон на травах, очень самобытный но при этом совершенно не пригодный к употреблению(если ты не алкаш у которого горят трубы). Поэтому купил я его только маленькую бутылочку.  Типа успокоительное для тех кто боится летать. На самом деле - так чтобы отпить, и без сожалению выбросить все остальное. Но напиток превзошел все мои ожидания. Это довольно приятный на вкус, сладенький анисовый ликерчик, градусов примерно 30. В общем всем рекомендую его.  

воскресенье, 2 февраля 2020 г.

Мониторинг GC(Garbage Collector) в Node.js без использования нативных модулей

       С пол года назад я послал в официальный prometheus клиент для Node.js pull request с реализацией мониторинга GC Node.js без использования нативных моделей. В  Node.js начиная с восьмой версии есть интерфейс perf_hooks который позволяет немного заглянуть под копот V8 и установить обработчик на некоторые события. Одно из этих событий - запуск сборщика мусора. Собственно подписавшись на него мне удалось собрать эту статистику и экспортировать ее в прометей в виде гистограммы https://github.com/siimon/prom-client/pull/274 
       Сегодня, спустя примерно полгода мне удалось пропихнуть его в мастер.

вторник, 28 января 2020 г.

Как Тиньков построил банк

         Дочитал книгу "Тиньков Революция. Как построить крупнейший онлайн банк в мире".  В общем книга представляет из себя легкое чтиво, с не хитро закрученным сюжетом. Если коротко - супер герой Олег попадает в разные трудные ситуации (банковские кризисы, санкции и тд), несколько раз оказывался на грани пропасти и не смотря ни на что построил банк всем ветрам на зло.  Из интересного, что я выписал для себя после прочтения:
  • Плоская управленческая структура, минимум иерархии и бюрократии
  • Банк много раз эволюционировал. От первоначального замысла в виде «почтового банка» рассылающего карточки почтой России к «интернет банку», привлекающему клиентов через онлайн. А затем превратился в «финансовый супермаркет» или как это сейчас можно называть «супер приложение»
  • Всю критически важную экспертизу имеют внутри. Не пользуются услугами рекламных агентств, а сами закупают трафик у площадок и сами оценивают его эффективность. Не пользуются аутсорс разработкой, а развивают собственную ИТ команду. Предпочитают использовать собственные ИТ решения, а не покупать коробочные продукты за много миллионов долларов
  • Любой рекламный канал рано или поздно умрет. Постоянно ищут новые рекламные каналы. Важным является не знание где сейчас дешевле трафик, а умение находить новые каналы и умение находить новые способы работы с рекламными каналами
  • Надо освежить в памяти темы:
    • Наивный байесовский классификатор
    • логическая регрессия
    • композиции деревьев
    • gradient boosting
    • кластеризация - EM алгоритм
    • обучение с подкреплением
  • Принципы ведения бизнеса
    • Нетолерантность к убыткам. В бизнес плане должен быть определён момент выхода в 0, и это должно быть не через 5 лет
    • Стабильность команды.
    • Личные качества первичны. В меньшей степени смотрит на профессиональные качества а в большей на человеческие/личностные
    • Параноидальность. Даже если ты достиг больших высот, не думать что ты уже всего достиг а ещё выше задирать себе планку. Стремление к перфекционизму. Не любит хвалить сотрудников.
    • Вовлечённость основателя решает
    • Горизонт планирования. Нужно думать хотябы на 5 лет вперёд
  • Прочитать - Питер Тиль, от нуля к единице
  • Обслуживание это всегда расходы. Сократить их - нормальное желание любого менеджера. Но важно почувствовать ту грань когда сокращение расходов ведёт к ухудшению качества обслуживания
  • 15% чатов обрабатывается чат ботом. У ozon.travel около 0.5-1%
  • 70% звонков обрабатываются на WebOffice - веб версии консоли колл центра, в которой работают сотрудники работающие из дома
  • Переход к модели маркетплейса, заработок на комиссиях минимум 50% прибыли 
  • Идея состоит в том что в любом бизнесе самая дорогая операция - привлечение клиентов. В cost per order расходы на маркетинг всегда самые большие(одни из самых больших). Причём с увеличением объёма продаж эти расходы даже в расчете на заказ как правило увеличиваются(исчерпание источника трафика). Соответсвенно если у тебя есть один продукт(банковский) и ты уже один раз заплатил за привлечение пользователя, ты также можешь продать ему что-то ещё и в этом случае стоимость превлечения пользователя в этом заказе будет 0. На этом и основана идея маркетплейса - положить рядом как можно больше продуктов которые могут быть интересны для клиентов банка, продавать им эти продукты и брать комиссию 
  • Нельзя доверять стартапам/компаниям в которых основатель/CEO сам не вложился в бизнес значительной частью своего состояния. Если ты сам вложишься своими кровными деньгами в бизнес которым управляешь - то ты совершенно по другому будешь относится к деньгам компании, совершенно по другому будешь работать на компанию (ты будешь бояться потерять свои деньги вложенные туда). И никакие опционы на 0,01% акций такого чувства ответсвенности не дадут 
Особенно меня зацепил конец книги - про "стартаперов" и отношение к деньгам. Это правда на 200%. Почему тот же Wildberries гораздо успешнее Озона ? потому что Wildberries строит основатели, люди который там все сделали за свои деньги, и поэтому там мотивация совсем другая, она на 2 порядка выше чем у наемного менеджера, владеющего 0.01% акций. Основатель вкладывает душу в свое детище, а CEO каждые 3-5 лет меняется. По большому счету наемному менеджеру хочется продержаться у руля по больше, чтобы побольше акций завестилось, и побольше позолотить себе парашут перед уходом. Можно сколько угодно говорить про мотивацию, стремление к успеху и тд - но KPI собственного обогащения - это единственное что заложена нам подкорку, то о чем мы думаем в первую очередь. Все остальные мотиваторы стоят на ступеньку ниже.

четверг, 16 января 2020 г.

ARC - Adaptive Replacement Cache / ZFS

          Одним из самых известных отличительных качеств ZFS является ARC кэш(Adaptive Replacement Cache). В теории он обеспечивает примерно в 2 раза более высокий cache hit rate по сравнению с классическим LRU.  Алгоритм ARC был предложен  Nimrod Megiddo and Dharmendra S. Modha и описан в следующем whitepaper - https://www.usenix.org/legacy/events/fast03/tech/full_papers/megiddo/megiddo.pdf 
          Как и все гениальное - это алгоритм довольно простой. Ребята подумали и поняли что в принципе есть две стратегии кэширования страниц - кэшировать страницы который были недавно прочитаны и кэшировать страницы к которым часто обращаются повторно. Не смотря на схожесть - эти стратегии противоречат друг другу. Для каких-то паттернов нагрузки важно кэшировать то что было прочитано недавно, для каких-то важно кэшировать только какой-то набор постоянно используемых данных. В реальной жизни эти два паттерна всегда присутсвуют одновременно.  И поэтому ребята предложили иметь два LRU списка - один для недавно прочитанных страниц, второй для часто используемых страниц. Алгоритм пытается адаптироваться к текущей нагрузке и динамически изменять размеры этих списков - отдавая предпочтение либо кэшированию недавно прочитанных страниц либо кэшированию часто используемых страниц. 
           Реальная реализация которая находится в module/zfs/arc.c немного сложнее теоретической: 
  • Она работает с блоками разного размера
  • Она адаптируется к memory pressure. Если памяти не хватает размер обоих списков снижается
  • Не все страницы могут быть вытеснены в каждый момент времени. Если есть внешние ссылки на страницу, она не может быть вытеснена.

среда, 15 января 2020 г.

Реализация AVL дерева в Solaris/ZFS

          Меня всегда интересовали реализации структура данных на практике. Возьмем к примеру AVL-tree. С точки зрения теории все довольно понятно - сбалансированное двоичное дерево, поиск за log n, вставка от константы до log n. Но кроме O(n) есть еще реальное время выполнения операций, и при не слишком большом N это время гораздо важнее чем теоретические выкладки. Да, если N - очень большое то O(n) решает, но в реальной жизни случаи с очень большими N не так часто встречаются. Поэтому практические приемы используемые при реализации той или иной структуры данных не менее важны чем сам алгоритм. Собственно, в чем особенности реализации AVL дерева в Solaris/ZFS ?
  1. Написано на голом C, без классов (как и все то что должно работать в ядре)
  2. Не полезная нагрузка "вставляется" в узлы дерева, а наоборот - структуры данных необходимые для реализации дерева внедряются в те объекты которые планируется класть в дерево. Если быть точным - это даже не объекты а структуры (в терминах C). В результате - и payload и сами структуры данных дерева образуют единых блок данных. В результате резко повышается memory cache locality, в два раза снижается фрагментация данных (в классической реализации узел дерева это одна структура данных, в ней сидит указатель на собственно payload)
  3. Рекурсия не используется, только итерации. Для этого каждый узел дерева содержит указатель на родительский узел. Даже на C рекурсия медленнее чем цикл. Другая причина - этот код должен работать в ядре, где размер стэка не большой.
  4. Реализация не содержит блокировок - предполагается что тот кто использует структуру данных сам знает - нужно ли синхронизировать доступ к ней и если это необходимо - взял нужную блокировку
  5. Реализация не содержит выделений памяти. Все функции принимают на вход указатели на предварительно выделенные блоки памяти там где это необходимо. За счет этого достигается:
    1. Предсказуемость времени выполнения того или иного вызова. Выделение памяти это довольно рискованная в парадигме kernel-программирования операция которая может занять непредсказуемое время и завершиться непредвиденным результатом
    2. Абстрагирование  структуры данных от используемых механизмов выделения памяти.
  6.  Используются разные структуры данных на 32 и 64 битных платформах. Вся работа с структурами данных ведется через макросы. На 64-битной платформе флаги и другие мелкие значения упаковываются в младшие биты указателей. На 64-битной платформе младшие биты указателей всегда равны нулю. 
  7. Данные которые используются в наиболее часто вызываемых функциях, например avl_find() располагают в первых 64 битах структуры данных. Таким образом гораздо выше вероятность того что эти данные уже находятся в кэше процессора
  8. Вместо обычных конструкций ветвления if (больше) then {right} else {left} при обходе дерева используется node = node->child[compare(value, data)]. Почему так ? потому что branch prediction процессора обрабатывает if then конструкции хорошо только тогда когда вероятность переходя по одной из ветвей сильно выше, например при обработке ошибок. В случае прохода по дереву вероятность  пойти по левой и правой ветки примерно одинакова, что будет приводить к miss-prediction и сбросу конвеера процессора. Конвеер современных CPU очень глубокий, поэтому сброс конвеера - это довольно затратная операция. Поэтому вместо этого указатели на левый и правый child помести в массив, и результат сравнения искомого значения с тем что хранится в текущей ноде используется как индекс для получения указателя на следующий элемент дерева. В исходном кода мне кажется это будет понятнее - https://github.com/zfsonlinux/zfs/blob/2476f103069e83e17d2c9cec4191af34a7996885/module/avl/avl.c#L256 

          

вторник, 14 января 2020 г.

Real User Monitoring по взрослому

            Real User Monitoring (RUM) это с первого взгляда довольно простая фигня которую можно чуть ли не за пол дня на коленке запилить. Но если копнуть глубже то понимаешь что большинство реализаций довольно ущербные - те же самые RUM от NewRelic или RUM от Pingdom. Не говоря уже про написанные на коленке варианты.
               Из того что заслуживает внимание - Doppler от Facebook Ребята добавляют в каждый N-й ответ кусок JS-а который выполняет загрузку небольшой картинки с рандомного домена - например  xmdmddddm.dns.facebook.com, тем самым проверяя пессимистичный сценарий полный DNS lookup + HTTP запрос + ответ. Потом загружают еще одну маленькую картинку с того же домена.  Вычтем результат второго эксперимента из первого и получим пессимистичное время DNS lookup.  Второе время - минимальное время загрузки ресурса с сайта FB.  Они также записывают каждый проведенный эксперимент и далее аггрегируют эти  данные по AS-number. Количество автономных систем относительно невелико (около 35к), так что в таком виде данные уже гораздо легче анализировать. 
              Но ребята из dropbox пошли дальше  - https://blogs.dropbox.com/tech/2020/01/intelligent-dns-based-load-balancing-at-dropbox/ . Они сделали свой аналог doppler, с аггрегировали полученные данные  и из них сделали latency-map интернета. Скормили этот map DNS серверам (они используют ns1.com) и используют для latency based балансировки траффика между своими датацентрами. В общем получилась конфетка. 

пятница, 10 января 2020 г.

ZFS monitoring - V2

         Добил наконец-то вторую версию мониторинга ZFS. В ней добавлены метрики получаемые через zpool iostat.   Так как мой первый пул реквест в телеграф до сих пор ждет своего счастливого часа - https://github.com/influxdata/telegraf/pull/6724, этот код пока живет в отдельной ветке - https://github.com/yvasiyarov/telegraf/tree/zfs_zpool_linux2 Все новые метрики также появились в dashboard -  https://github.com/yvasiyarov/zfs-dashboard Теперь он так порядочно распух:

четверг, 9 января 2020 г.

Из интересно-прочитанного

       Довольно интересная статья про монорепу в Яндексе - Arc — система контроля версий для монорепозитория. Доклад Яндекса. Прочитав статью понимаешь что никакая это не система контроля версий - это проложение которое работает поверх SVN и по сути занимается его масштабированием.  Вся монорепа по прежнему лежит в SVN, который обеспечивает единственную операцию - commit в trunk. На Arc лежит масштабирование клиентских операций - их фактически перенесли на сервер, работа с pull реквестами и тд. Пул реквестов в оригинальном SVN нет, но ребятам очень хотелось их. Вот они и навертели их поверх SVN. Но если серьезно - мне нравится их подход к решению проблемы. Он проблемно-ориентированный. Они не сказали - надо все выкинуть к херам и два года пилить свою собственную систему управления исходным кодом, а взяли и решили конкретные проблемы с которыми они сталкивались с минимальным риском для компании. Ведь по сути максимум чем они рискуют - это коммитами в рабочих ветках. А эти данные полюбому сохраняются минимум еще в одном месте - на машине разработчика. Как только ветка вливается в монорепу - за ее сохранность отвечает SVN.
      Прочитал перевод интересной статьи про использование unsafe.Pointer и то какие при этом могут возникать проблемы - https://4gophers.ru/articles/unsafe/#.XhcssCq3p26