среда, 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 балансировки траффика между своими датацентрами. В общем получилась конфетка.