Перейти к основному содержимому

кино/The Wild Robot

· 1 мин. чтения

Очень хорошая анимация и музыка, местами 10/10 где есть сочетание всех элементов в том числе музыки - я аж всплакнул. Советую сходить, детям подойдет.

https://www.imdb.com/title/tt29623480/

Из недостатков - местами с ритмом повествования приходят странности, возникает растянутость и предсказуемость, нелогичность или не проработанность мира. Мол хочу обратно - не хочу обратно - сожгу все - опять хочу. Художественные условности - робот у которого любые отсеки, жгутики и механизмы появляются на ходу а память неизвестно как работает.

Кажется что картина пытается сделать слишком много за раз. Тут и про семью, и про взросление, и про мир во всем мире, тут и боевик, тут и жестокая комедия и техно-утопия.

Product team models - project based, feature factory and product operating model

· 4 мин. чтения

Listened to the podcast https://www.tpg.ee/p/product-team-models-project-based

Was interesting to listen and reflect how organizations tend to have indeed different structures. I suppose the project-based is a tendency in small and outsourcing companies, while more mature product-orinented companies go with feature-factories. I don't think I've seen a good implementation of the described product operating model myself. It seems hard to do such a company-wise mindset shift because people are narrow-focused on their role and thinking outside the box requires jumping through multiple layers. Even if companies are matrix-shaped and have vertical teams, they are not representing all layers. For example sales, marketing, support and infrastructure likely won't be part of the the team.

So I do wonder how does Aive Uus break those mindsets 😄 Hopefully we'll see more of this

Как записывать экран с круглой камерой

· 1 мин. чтения

Часто приходится по работе записывать видео скринкасты для репорта багов, описания глубоких сценариев их возникновения, неудобного UI; Кроме того я использую видео для описания pull-request'ов в дополнение к тексту.

Как сделать результат более профессиональным? Используйте камеру что-бы люди видели ваши эмоции. Это особенно важно в современной асинхронной коммуникации где все работают удаленно.

Asuur Keraamika

· 1 мин. чтения

Are you planning a team building event and bored of bowling and billiard?

Try pottery. Clay is like playdough and looks simple, but it's like programming and can reveal personal taste. You have a product vision, tight deadlines and extra features you want to include.

You can go straight in and fail fast to quickly recover (like I did) . Or you can plan ahead, cut different parts and assemble it like a puzzle, waterfall-style. Naturally you must be aware of tools and integration tricks.

Finally you ship it for glazing and hope that users will find it useful and beautiful

Thanks Artjom Shestajev for organizing Clarifai Estonia event at asuurkeraamika.com

CUDA

· 1 мин. чтения

Послушал случайную онлайн лекцию по CUDA и поскольку это интересно, скопировал себе немного слайдов

(From public video stream workshop)

Katana meetup

· 1 мин. чтения

Katana Cloud Inventory & ProductTank (Anna Albert + Olesia Kucheryk) hosting AI meetup ( https://lnkd.in/dhUbzDE6 )

Got to listen about opengenie.ai, Align, Databricks usage to analyze company data by auto generating SQL, summarization of support, customer research, lead/demo calls. Thx to Andres Aava. Reminded me a bit about Supersimple.

Second talk was about EU AI Act by Krete Paal
https://lnkd.in/dgDZEGcd

Personally, I see more and more risks with AI getting used. Privacy of LLM calls,
AI bots in youtube comments, bots creating meaningless PRs (looking at dependabot), creating fake and hallucinated issues (like CURL incident - https://lnkd.in/dUmR3ucK ) not to mention health ( https://lnkd.in/dCkAPVVE )

I was curious about

  1. Katana's plan to have native AI features
    (for example prior Pipedrive meetup mentioned model generation per customer)
  2. Level of trust to generated queries
    (as someone using code generation daily)
  3. AI act loopholes
  4. how do companies pass info about AI and privacy compliances in its value chains (reminded me of npm and other package managers)

Login to Meetup | Meetup

Photos from meetup group:

Pipedrive meetup

· 1 мин. чтения

Listening to Elizaveta Lebedeva talk on fraud detection reminded me of similar talk at Wise couple of years ago.

Maybe such anomalies and trends could be shared? An idea for a fraud/security startup 😉

Applied Machine Learning at Scale // a meetup by Pipedrive and PyData

Login to Meetup | Meetup

Digit Tartu conference 2024

· 2 мин. чтения

At Digit Conference

Jaroslav Judin expanded on prior credit-card anti-fraud talk that Elizaveta Lebedeva did. This time focus was on redis. Sandbox - https://lnkd.in/d78gmwZm

Swedbank talk by Lehar Oha & Markus Reimegård was somewhat high-level about adding AI (similar to what Clarifai does as a service). As I understood, pdf parsing for RAG might be hard, just to get right embeddings. But I hope I will be able to ask Swedbank GPT about my contracts (leasing or insurance) for preliminary analysis. Just add a notice that it's an automated tool doing an analysis.

Dmytro Hnatiuk talked about databases. The insight there as I understood is that its similar to Security. If you have DB not performing well, its not that much of DB engineer problem, its developer's problem. Developers need to think in advance about storage, queries, sharding, domain separation, archival etc.

Arne Ansper was advocating for UML and state machines. I sort of like to document high-level things with mermaidjs, but at times I question if UML is that intuitive (and that you should not reinvent new modelling language), if it needs to be source of truth with code generation or that one needs to first model the system with RUP
https://lnkd.in/da-sxSCn

Eddy Vinck was promoting Astro https://astro.build/ - a javascript framework to generate static websites with some extensions. I was thinking of migrating my blog and merging it with second-brain data from notion/obsidian and publishing that as a static website to have less CMS/DB management overhead. Maybe this static generation would help, but I'm worried about search, including vector search for recomendations.

https://www.flickr.com/photos/184496731@N07/54052222680/in/album-72177720321002499

![[img/Pasted image 20241014130910.png]]![[img/1000022433.jpg]]

![[img/1000022435.jpg]]

![[img/1000022416.jpg]]

![[img/1000022414.jpg]]

![[img/20241004_130406.jpg]]

![[img/20241004_124837.jpg]]

Tallinn JS meetup

· 2 мин. чтения

Attended TallinnJS meetup organized by Stefano Amorelli at LIFT99. Won ticket to Digit Conference. Almost won a hoodie from Printify but figured that googling an answer was not fair 🤐

First talk was by Dmitrii Baranov about telegram bots using telegraf.js telegraf.js - v4.16.3

Second talk was by Jan Johannes about workerd, a js runtime
GitHub - cloudflare/workerd: The JavaScript / Wasm runtime that powers Cloudflare Workers

I think having a bot is a nice interface extension to any app, in addition to the usual web, mobile, desktop and cli apps. Telegram, discord, slack bots can improve support reaction time, push app alerting/notifications and more. I wish telegraf supported text and video streaming though.

Then it could also be more seamlessly integrated with LLMs, say if you had a Clarifai bot streaming AI questions and answers, switching ML model on the fly would you use it over a web UI? With bots and AI element to it, apps could become much more interactive and lose UI entirely. You can see that change on the example of google search -> perplexity & chatgpt transition.

Regarding workerd, its quite a niche engine that I feel does a bit too much (I would not want to re-learn k8s), but it may be useful in cases when you want your app users to run custom code, so you could allow some javascript sandbox to run closer to your dataserver, instead of using webhooks. For example Pipedrive marketplace could allow external app vendors to run custom js code on its backend, similar to how go playground, coderpad, leetcode, github or other platforms allow running custom code.

Об этике пчеловодства

· 3 мин. чтения

Эрнест Бондаренко, мне вот захотелось так же публично ответить тебе, раз латвийская команда ForQueenBee на презентации в Prototron не смогла, и естественно Jana Budkovskaja не могла им ( Guntis Laurins,  Anete Grosberga ) дать дополнительного во время общего питча.

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

Dream of scalable & enriched graphql-subscriptions

· 12 мин. чтения

Originally posted in Medium

https://miro.medium.com/max/1400/1*Rvb94EOQA-BzsmYJ4TklGg.jpeg

Stylized photo of Jagala juga (Estonia), original photo by Aleksandr Abrosimov, Wikimedia Commons

Last time, I wrote about 5-year long journey of having GraphQL in Pipedrive. Now, let me tell you about a 10-year long journey of delivering websocket events to frontend.. and maybe help you out with it too.

https://miro.medium.com/max/1400/1*xffiu2fshUMbBFm-JlSAGw.gif

Journey to a Federated GraphQL

· 15 мин. чтения

Original post at medium

https://miro.medium.com/max/1400/1*6ADAF1r2LFunXgdklnMRzw.png

Image taken from dgraph.io

Engineers tend to love good stories, so hopefully our 5-year journey of moving towards API composition with GraphQL now in production (serving at peak 110 requests per second at 100ms latency) provides a good story.

[If you’re in a hurry, scroll down to Lessons learned and check out the open-sourced graphql-schema-registry.]

https://miro.medium.com/max/1400/1*d32xg4FUICDlOm9q0PCLkw.png

schema registry with some example schema

Путь к Федеративному GraphQL

· 14 мин. чтения

Картинка с dgraph.io

Программисты любят хорошие истории, поэтому надеюсь что пятилетний путь к композитному API с помощью GraphQL в боевой среде (на пике выдающей 110 запросов в секунду при 100мс задержке) будет интересен.

[Если вы спешите, проскрольте ниже к урокам и гляньте на открытый код graphql-schema-registry]

schema registry с примером тестовой схемы

Задача

Годами, Pipedrive (которому в начале 2020 стукнуло уже 10 лет), давал публичный REST API где были и недокументированные пути для нашего веб приложения. Один из них /users/self, который изначально задумывался для загрузки информации о пользователе, нормально с течением времени стал грузить все что надо для первой загрузки страницы, доходя до 30 разных типов сущностей. Сам он выдавался PHP монолитом, который по натуре синхронный. Мы пытались его распараллелить, но неудачно. /users/self распределение задержки для оставшегося траффика

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

Прототип с прямым доступом к БД

Давайте вернемся назад в прошлое, когда наши разработчики стали экспериментировать с graphql.

Года 3-4 назад, в команде разработки рынка приложений, я услышал от Паши, нашего фулл-стек разработчика новенькие для меня термины - elixir и graphql. Он участвовал в тестовом проекте в котором напрямую работал с MySQL и выдавал основные сущности Pipedrive по /graphql.

На локальной машине работало прекрасно, но это не масштабировалось на весь код, потому что функционал - не обычный CRUD, а весь монолит переписывать никто не хотел.

Прототип со склейкой

Переносимся в 2019, когда я заметил еще один тестовый репозиторий от другого коллеги, который стал использовать GraphQL stitching с определением сущностей с помощью graphql-compose и резолверами которые делали запрос уже к нашему REST API. Как можете представить, это уже было значительным улучшением, потому что нам не надо было переопределять всю бизнес-логику и graphql становился просто приятной оберткой.

Из недостатков этого решения:

  • Производительность. Небыло dataloader'а, поэтому был риск N+1 сетевых запросов. Небыло ограничения сложности запроса и какого-либо промежуточного кеширования.

  • Управление схемой. При склейке типов, мы определяли все сущности в одном репозитории, отдельно от самих сервисов которые этими даннами владеют. Это усложняло деплой - приходилось бы писать обратно-совместимые изменения, что-бы избегать несовпадения схемы и функций.

Подготовка

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

Сбор ожиданий разработчиков

В компании некоторые разработчики были довольно скептичны и предлагали построить свое решение просто склеивая запросы воедино, отправляя по POST и надеясь на свой gateway который будет распараллеливать обработку.

Другие считали что graphql еще слишком сырой что-бы использовать в жизни и лучше подождать. Третьи предлагали рассмотреть альтернативы, типа Protobuf и Thrift, а в качестве транспорта рассмотреть GRPCOData.

Четвертые напротив, на полном ходу писали graphql под одиночные сервисы (insights, teams) и выкатывали в лайв, однако не могли повторно использовать сущности (например User). В Праге ребята написали все на typescript + relay на фронтенде, который нам еще предстоит перенести в федерацию.

Изучать новую технологию было круто.

Строгий, само-документирующийся API для фронтендеров? Глобально определяемые сущности уменьшающие дублирование и улучшающие прозрачность и владение среди всех команд? Gateway который автоматом делает под-запросы и между сервисами без избыточной выборки? Ухх.

Впрочем, я знал что нам понадобится управлять схемой как-то динамически, что-бы не полагаться на захардкоженые значения и видеть что происходит. Нечно типа Confluent’s schema-registry или Atlassian’s Braid, но они либо под Кафку делались, либо на Java, которую мы не хотели поддерживать.

План

Я выступил с инженерной миссией у которой было 3 цели:

  • Уменьшение первичной загрузки страницы (воронка продаж) на 15%. Достижимо если заменить некоторые REST запросы в один /graphql

  • Уменьшение траффика на 30%. Достижимо если перенести загрузку всех сделок для воронки продаж в graphql и спрашивать меньше свойств.

  • Использовать строгую типизацию (что-бы фронтендерам надо было писать меньше кода в защитном стиля)

Мне повезло, трое разработчиков присоединились к миссии, в т.ч. автор прототипа.

Сетевые запросы по мере загрузки веб-приложения

Изначальный план по сервисам выглядел так:

Сервисы над которыми предстояло бы работать

Тут schema-registry был бы общим сервисом, который мог бы хранить схему любого типа, получая на входе что вы в него кинете (swagger, typescript, graphql, avro, proto). На выходе он мог бы так же выдавать что вы хотите.

Gateway периодически опрашивал бы schema-registry и вызывал бы уже сервисы которые владеют данными согласно запросу и схеме. В свою очередь frontend компоненты могли бы скачивать актуальную схему для autocomplete и самих запросов.

На деле впрочем, мы сделали только поддержку graphql формата, потому что для федерации его было достаточно, а время как обычно быстро закончилось.

Итоги

Главную цель по замене /users/self в веб-приложении мы сделали в течение первых двух недель ? (ничоси!). А вот полировка всего решения что-бы оно быстро и надежно работало, заняло все оставшееся время.

К концу миссии (в феврале 2020), мы каким-то чудом достигли 13% ускорения первой загрузки страницы и 25% ускорения при повторной загрузке (из-за добавленного кеширования), согласно синтетическому UI тесту который мы гоняли на Datadog.

Уменьшить трафик нам не удалось, потому что мы не достигли рефакторинга вида воронки продаж - там все еще REST.

Что-бы ускорить добавление сервисов в федерацию другими командами (у нас 600+ человек), мы записали обучающие видео что-бы все были в курсе как оно все работает вместе. После окончания миссии IOS- и Android- клиенты тоже мигрировали на graphql и в целом были рады.

Выученные уроки

Глядя на дневник миссии из этих 60 дней, могу выделить наибольшие трудности что-бы вам было бы попроще

Управляйте своей схемой

_Могли бы мы построить это сами? Возможно, но это не было бы так отполировано.
_Mark Stuart, Paypal Engineering

В течение первых пары дней я попробовал Apollo studio и их инструментарий для терминала по проверке схемы. Сервис прекрасный и работает из коробки с минимальными настройками gateway.

Написанный нами инструментарий для валидации схемы

Написанный нами инструментарий для валидации схемы

Однако несмотря на все фишки этого облачного сервиса, я посчитал что для такого важного дела как маршрутизация траффика было бы опрометчиво полагаться на сторонний сервис из-за рисков для жизнеспособности нашего дела, несмотря на богатый функционал и сколько-то выгодные расценки. Поэтому мы написали свой сервис с минимально нужным функционалом и теперь отправили его в opensource -  graphql-schema-registry .

Вторая причина была в том что-бы следовать модели распределенной архитектруры датацентров Pipedrive. У нас нет центрального датацентра, каждый DC самодостаточен. Это дает нам как более высокую надежность, так и способность открывать новые датацентры в Китае, России, Иране или на Марсе ?

Версионируйте свою схему

Федеративная схема и graphql gateway очень хрупкие. Если у вас появится конфликт имен у типов или неверная ссылка между типами в одном из сервисов, то gateway может этого не пережить.

По умолчанию, gateway напрямую опрашивает все сервисы касательно их схемы, поэтому достаточно одного из сервисов с ошибкой что-бы поломать всем трафик. Apollo studio решает это посредством проверки схемы еще до деплоя и отказе если видит какой-то конфликт.

Проверка совместимости схем правильный подход, но конкретное решение у Apollo зависит от их внутреннего состояния, т.к. они хранят правильную склеенную схему на данный момент. Это делает протокол регистрации сложным и зависящим от времени.

Мы же напротив, связали версии микросервисов (генерируемые на основе хешей докер-образов) со схемой. Микросервисы регистрируют уже в ходе своей работы и делают это один раз без последующих повторений. Gateway делает выборку всех федерированных сервисов из консула и спрашивает schema-registry составить схему /schema/compose , предоставляя их версии.

Если schema-registry видит что предоставленные версии несовместимы, она откатывается на прошлые версии

Регистрация схемы при старте сервиса

Микросервисы могут выдавать как REST так и Graphql API, поэтому мы используем оповещения тревоги при неудачной регистрации схемы, оставляя при этом работу REST.

Определение схемы на основе REST не просто

Поскольку я не знал как лучше перенести схему из нашего REST в graphql, я сначала попробовал openapi-to-graphql, нормально наша документация на тот момент не имела детального описания запросов и ответов.

Спрашивать у каждой команды что-бы они написали бы ее, очевидно заняло бы уйму времени, поэтому мы определили основные сущности сами ? просто глядя на ответ при разных REST запросах.

В дальнейшем это нам аукнулось, так как оказалось что REST API иногда зависит от того кто делает запрос, либо зависит от внутреннего состояния и логики.

Например после настраиваемые поля данных меняют REST API в зависимости от пользователя. Если его добавить к сделке, то он оно будет выдаваться с ключем-хешем на уровне deal.pipeline_id. С федеративным graphql, динамическая схема невозможно, поэтому нам пришлось передвигать эти настраиваемые поля в отдельный список

Другая проблема это стиль наименований в json-ответе. Мы хотели поддерживать верблюжийСтиль, но почти весь REST отвечал нам в змеином_стиле, пришлось пока остаться на смешанном.

федеративный граф данных Pipedrive (слева) с 2 сервисами (из 539) и еще-не-федеративный граф сервиса leads (справа)

CQRS и кеш

Модель данных в Pipedrive не может полагаться на простой TTL-кеш.

Например, когда инженер поддержки создает глобальное оповещение о предстоящем техническом обслуживании приложения из нашей админки, он ожидает что сообщение будет сразу же видно пользователям. Эти глобальные оповещения могут быть адресованы как всем пользователям, так и конкретным компаниям и индивидуальным людям. Чистить такой кеш приходится используя 3 разных ключа.

Что-бы делать параллельные запросы к PHP-монолиту, мы написали сервис на nodejs (назвали его monograph), который кеширует все что отвечает монолит в memcached. Этот кеш надо чистить из монолита в зависимости от бизнес-логики. Это немного антипаттерн, потому что кеш получается общим для разных сервисов и сильно их связывает.

Тут можно заметить CQRS pattern. Впрочем, такой кеш позволяет ускорять 80% запросов и дает итоговую среднюю задержку такую же как и у монолита.

Время средней задержки ответа монолита (слева) и graphql gateway (справа) в регионе US на основе NewRelic

Еще одна сложность - язык пользователя. Как только он меняется, нам надо чистить уйму разных сущностей - от типов активности до того какие настройки должны быть при показе гугл карт. И что еще хуже - язык уже управляется не в монолите, а был вынесен в отдельный сервис identity. Связывать его с общим кешем очень не хотелось.

Identity владеет данными пользователей, поэтому мы сделали так что он кидает событие через kafka, которое ловит monograph и чистит все нужные кеши. Тут есть недостаток, что событие приходит с задержкой (может максимум 1 сек), поэтому чистка кеша происходит не мгновенно. Нормально это не критично, потому что пользователь не так быстро уходит со страницы что-бы сделать повторный запрос к бекенду и заметить что в кешах еще старый язык.

Следите за производительностью

Производительность была главной целью и для этого нам пришлось хорошо прокачаться в знании APM и выслеживании распределенных запросов между микросервисами что-бы определить самые медленные места. Мы на тот момент использовали Datadog и он хорошо себя зарекомендовал.

Для ускорения, мы кешировали все 30 параллельных запросов в memcached. Но по ходу дела возникали проблемы. Например на картинке слева видно что некоторые резолверы делают запрос в memcached и получают ответ за 10мс, а другие тупят, словно жду своей очереди выростая до 220мс. Как оказалось, это было из-за того что я использовал один и тот же хост mcrouter. Как только я стал их ротировать, мемкеш стал отдавать все за 20мс максимум.

Что-бы уменьшить количество этих сетевых запросов к мемкешу, мы склеили его в один используя getMulti функцию. Получается что несмотря на то что все resolver'ы выполняются независимо, они блочатся на 5мс с помощью debounce и склеиваются воедино.

На картинке справа видна другая проблема. Эти желтые линии это налог graphql gateway на приведение данных к строгому типу. Причем чем больше данных вы гоняете через сервис, тем дольше будет эта фаза выполнятся.

Вобщем по ходу миссии постоянно приходилось мониторить какие же запросы нам надо еще закешировать и под конец стало бесить что скажем 28 из 30 запросов прекрасно кешируются, а оставшиеся два мало того что отвечают целых 500мс, так их еще и невозможно легко закешировать.

Нам пришлось вынести их в отдельный graphql запрос, который делается после первого /graphql запроса на инициализацю страницы. Так что сейчас мы на деле делаем уже порядка 3-5 независимых запросов, в зависимости от тайминга фронтенда (там тоже debounce зависящий от параллельной подгрузки FE компонентов)

Не следите за производительностью

Казалось бы полностью противоположный урок, но на деле это значит что в лайв-среде вам лучше отключить мониторинг приложения (APM) и tracing:true, если вы используете их.

Отключение отслеживания у нас ускорило среднюю задержку в два раза с 700мс до 300мс для нашей тестовой компании. Насколько я понимаю, причина крылась в функциях которые замеряют время (типа performance.now()), которые необходимы для замера каждого резолвера.

Мы делали профайлинг graphql gateway с помощью  Chrome DevTools, но так просто вызов этих мелких функций, раскиданных повсюду не заметишь.

Бен тут делал замеры и тоже пришел к этому.

Попробуйте ранний запрос

На фронтенде, время создания graphql запроса для нас оказалось сложновато. Я хотел передвинуть первый /graphql как можно раньше (даже до загрузки всего кода в vendors.js) в графике сетевых запросов. Мы смогли это сделать, но веб-приложение из-за этого стало значительно сложней.

Что-бы делать запрос вам понадобится graphql клиент и парсинг gql литералов для определения самого запроса. Дальше его надо либо выдавать в отдельном bundle, либо отказываться от всех удобств и использовтать сырой fetch. Даже если делать сырой запрос, дальше надо что-то делать с ответом - распределять данные по моделям либо сохранять в глобальную переменную. Вобщем мы решили отказаться от этого и надеяться на server-side-rendering и service workers в будущем.

Перенос /graphql запроса левей

Оценивайте сложность запроса

Чем graphql примечателен, так это то что тут можно оценивать насколько больно запрос клиента будет бить по инфраструктуре не делая при этом еще никакой работы. Для оценки сложности надо указывать цену внутри самой схемы с помощью директив, а дальше делать саму проверку при получении запроса и отклонять его если сложность слишком велика, подобно нашему rate limit на частоту запросов.

Сначала мы попробовали graphql-cost-analysis библиотеку, но в итоге написали свою, поскольку захотели больше фич - множителях при выдаче списков данных, вложенности и рекурсии, а так же определении типов влияния на инфраструктуру (сетевой запрос, БД, CPU, работа с файлами). Тут сложней всего оказалось внедрять поддержку своей директивы в gateway и schema-registry. Надеюсь мы нашу библиотеку тоже опубликуем.

У схемы много лиц

Работать со схемой в js/typescript на низком уровне порой мучительно. Это начинаешь понимать когда пробуешь интегрировать существующий graphql сервис в федерацию.

Например настройки koa-graphql и apollo-server-koa ожидают что вы создадите вложенный объект  GraphQLSchema  который включает и сами резолверы. Но федеративный apollo/server хочет схему и резолверы отдельно:

buildFederatedSchema([{typeDefs, resolvers}])

В другом случае, как я уже писал, вам захочется определить схему с помощью gql литерала, или сохранить схему в schema.graphql файл, а когда вам надо будетделать проход по всей схеме для оценки сложности, понадобится ASTNode (и функции parse / buildASTSchema)

Плавная canary деплой

Во время миссии мы выкатывали изменения сначала всем разработчикам что-бы отследить очевидные ошибки.

Под конец миссии в феврале, мы выкатили graphql только на 100 компаний-"везунчиков". Дальше мы медленно выкатывали их до 1000 компаний, потом 1%, 10%, 30%, 50% и наконец в июне выкатили всем.

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

Учитывая насколько большие изменения мы делали, это был хороший опыт быстро получить обратную связь, найти баги (с кешированием), при этом снизив риски для большинства пользователей.

Надежды и мечты

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

Как только наш graphql API станет стабильным и достаточным для использования любыми пользователями, его можно выкатить в качестве второй версии нашего API. Для публикации впрочем понадобятся еще директивы для ограничения доступа к сущностям согласно OAuth видимости (для приложений из рынка) и согласно нашим продуктам.

В самой schema-registry надо добавить отслеживание клиентов, связать ее gateway для получения аналитики трафика что-бы проще было понять какие поля можно удалять, надо добавить фильтрацию и подсветку сущностей в зависимости от директив цены и видимости, валидировать именование, хранить постоянные запросы (persisted query), публиковать историю изменений итоговой схемы для сторонних разработчиков.

Кроме того, поскольку у нас есть сервисы на go, еще непонятно как стоит делать внутренний трафик - стоит ли использовать GRPC из-за скорости и надежности, либо использовать независимые graphql эндпоинты, либо ставить централизованный внутренний gateway. Если GRPC лучше только из-за его бинарности, может мы можем написать библиотечку которая тоже упакует graphql данные с помощью msgpack?

Что касается внешнего мира, надеюсь Apollo со своим проектом Constellation ускорят обработку запроса на Rust что-бы небыло этих 10% налога на производительность и смогут федерировать graphql сервисы без серьезных изменений последних.

Вобщем прекрасное время что-бы наслаждаться разработкой где уйма сложности!

Техническое интервью как нейрон

· 4 мин. чтения

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

Шпаргалка по golang

· 3 мин. чтения

После php и node начал писать на go, поэтому по-аналогии с unix-шпаргалкой, выпишу для себя основы..

Запуск

go run main.go → компиляция и запуск exe
go build main.go → только компиляция и создание exe, без запуска
go get -u https://github.com/x/y → импортирование зависимостей

Переменные и типы

типдекларациязначение по умолчанию
целыеint8, int 16, int32, int64, uint, uint640
бинарныеbyte
битboolfalse
с плавающей точкойfloat, float32, float640
строкаstring, rune(пустая строка)
var i int = 10;
var myfirstvar, mysecondvar int = 3, 4; // объявление разом
var autoInt = -10 // компилятор сам подберёт тип
var mystring = "test\n" // двойные для строк, всё в уникоде
var mysymbol = '\u2318' // один символ
var rawBinary = '\x27' // одинарные кавычки для символа

myNewDeclared := 42 // быстрая инициализация
myMathVar = 7+3i // математикам

Приведение типов

int(myfloat)
float64(myInt)
string(48) //будет номер символа, а не строчка "48"
bin := []byte(mystr) //перевод строки в массив байтов для итерации по байтам

Константа

const fullName = "Artjom"

//объявление нескольких переменных с инкрементом
const (
one = iota + 1 //iota начало итерации с нуля
_ // пропуск "двойки"
KB uint64 = 1 << (10*iota)
)

Массив

Низкоуровневый тип. Длина массива фиксирована.

var a1 [3]int //длина массива, сразу заполнится [0 0 0]
var a1 [2*size]bool //длина зависит от перменной
a3:=[...]int (1,2,3) //инициализирование значениями
var aa[3][3] int //массив массивов

len(mystring) //длина строк и массивов

Слайс (резиновый массив)

Более сложная структура. Состоит из массива с фиксированной длиной (capacity) в которую записано меньшее количество элементов (length). Как только length превышает capacity, размер capacity удваивается

var s1 []int //без размера
append(s1, 100) // добавление
len(s1)
cap(s1) // узнать capacity
s2:= []int(1,2,3,4); //создание со значениями

s1 = append (s1, s2...) //Соединение слайсов через оператор развёртывания значений
s3 := make([]int, 10) //Создание слайса сразу нужной длины, значения уже будут заполнены
s4 := make([]int, 10, 15) // Создание слайса с нужной capacity
copy(s7, s6) // копирование значений, но размер целевого слайса должен совпадать
fmt.Println( s7[1:5] ) // часть слайса

// слайс из массива
a:=[...]int(5,6)
b:=a[:] //слайс теперь ссылается на массив

Мап (ассоциативный массив)

var myEmptyMap map[string]string // [тип ключа] и тип данных, но пустой, в него нельзя писать

var mm1 map[string]string = map[string]string{} //пустой интерфейс
mm2 := map[string]string{}
mm3 = make(map[string]string)

mm2["firstName"] = "Artjom" //write
fmt.Println(mm2["firstName"]) //read
delete(mm3, "somekey") //delete

fmt.Println(mm3["nonexistingkey"]) //выдаст значение по умолчанию (пустая строка)

_, exists = mm3["missing"] // проверка есть ли такой ключ

Ключевые слова в управляющих конструкциях

  • fallthrough - проваливание в switch case

  • break - выход из  switch

  • range - итерирование по символам строки, например

  • for indexval := range mystr

Функция

init() - вызывается при старте программы в любом из пакетов (порядок неконтролируемый)

//аргументы передаются по значению
func avg(x int) float{ //возвращаемый тип в конце
}

func multipleArgs(severalInts ...int){ //развёртывание аргументов в слайс
}

func avg2(y int) (res float){ //сокращение return res в конце
res := 3
}

func mysql(file File, bool x){
file.open()
defer file.close() //заранее объявляет что надо вызвать в конце исполнения функции

if(x) return 0
//do more lots of stuff
return 1
}

См. также

Dashboard на основе Grafana и InfluxDB

· 2 мин. чтения

В продолжение темы умного дома где мой котёл умел выдавать API для мониторинга температур, захотелось мне вывести эти данные в более приятный вид. Кроме того, хотя сам котёл умеет рисовать графики, он показывал на них только температурную зону одного контура, а мне хотелось видеть два.

Поэтому я решил поизучать бесплатный dashboard и графико-генератор на основе Grafana. Зарегился и упёрся в то что он сам не хранит данные. Он умеет только подключаться к внешним источникам и делать запросы туда. 

Новый MySQL мне не хотелось поднимать, а открывать существующий тем более. Cloudwatch от Амазона - видимо полезен для мониторинга сервисов если вы активно пользуетесь этой инфраструктурой. Prometheus я могу и на работе посмотреть. Выбрал InfluxDB - специальную БД для хранения временных событий.

Ставить Influx было достаточно просто. Сложней было связать графану с influx.

Сервис гоняется на 8086 порту и Grafana хочет SSL и CORS. Поэтому пришлось делать proxy на nginx и добавлять header:

location /influx/ {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header 'Access-Control-Allow-Credentials' 'true';

proxy_pass http://localhost:8086/;
}

Теперь встаёт вопрос как данные добавить в influx. Писать будет процесс на node, поэтому я по-быстрому нашёл библиотечку и пример подключения: 

const Influx = require('influx');

const influx = new Influx.InfluxDB({
host: 'localhost',
database: 'home',
schema: [
{
measurement: 'response_times',
fields: {
boiler_smoketemp: Influx.FieldType.FLOAT,
boiler_output: Influx.FieldType.FLOAT,
boiler_power: Influx.FieldType.FLOAT,
boiler_light: Influx.FieldType.FLOAT,
boiler_oxygen: Influx.FieldType.FLOAT,
boiler_oxygenlow: Influx.FieldType.FLOAT,
boiler_oxygenmid: Influx.FieldType.FLOAT,
boiler_oxygenhigh: Influx.FieldType.FLOAT,
boiler_connectionindex: Influx.FieldType.FLOAT,
boiler_return: Influx.FieldType.FLOAT,
zone_1: Influx.FieldType.FLOAT,
zone_2: Influx.FieldType.FLOAT
},
tags: [
'host'
]
}
]
});

//заполняем data..

influx.writePoints([
{
measurement: 'heating',
fields: data
}
]).catch(err => {
console.error(`Error saving data to InfluxDB! ${err.stack}`)
});

Теперь что-бы проверить записались ли данные, можно использовать хронограф - скачиваемый UI-сервис который в более ранних версиях был встроен в influx в виде админ-панели.

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