Русский
Русский
English
Статистика
Реклама

Блог компании miro

Перевод Prometheus и VictoriaMetrics отказоустойчивая инфраструктура для хранения метрик

08.12.2020 14:22:23 | Автор: admin

В статье мой коллега Luca Carboni, DevOps Engineer из амстердамского офиса Miro, рассказывает, как выглядит наша инфраструктура для хранения метрик. Все компоненты в ней соответствуют принципам высокой доступности (High Availability) и отказоустойчивости (Fault Tolerance), имеют чёткую специализацию, могут хранить данные долгое время и оптимальны с точки зрения затрат.

Стек, о котором пойдёт речь: Prometheus, Alertmanager, Pushgateway, Blackbox exporter, Grafana и VictoriaMetrics.

Настройка High Availability и Fault Tolerance для Prometheus

Сервер Prometheus может использовать механизм federation, чтобы собирать метрики с других серверов Prometheus. Он хорошо работает, если вам нужно открыть часть метрик инструментам вроде Grafana или нужно собрать в одном месте метрики разного типа: например, бизнес-метрики и сервисные метрики с разных серверов.

Такой подход широко применяется, но не соответствует принципам высокой доступности и отказоустойчивости. Мы работаем лишь с частью метрик, а если один из серверов Prometheus перестанет отвечать, то данные за этот период собраны не будут.

Готового встроенного решения этой проблемы не существует, но для её решения не обязательно настраивать сложные кластеры и придумывать сложные стратегии взаимодействия серверов. Достаточно продублировать конфигурационный файл (prometheus.yml) на двух серверах, чтобы они собирали одни и те же метрики одинаковым способом. При этом сервер A будет дополнительно мониторить сервер B и наоборот.

Старый добрый принцип избыточности прост в реализации и надежён. Если мы добавим к нему инструмент IaC (инфраструктура как код) вроде Terraform и систему управления конфигурациями (CM) вроде Ansible, то этой избыточностью будет легко управлять и легко её поддерживать. При этом можно не дублировать большой и дорогой сервер, проще дублировать маленькие серверы и хранить на них только краткосрочные метрики. К тому же, небольшие серверы проще воссоздавать.Alertmanager, Pushgateway, Blackbox, экспортёры

Теперь посмотрим на другие сервисы с точки зрения высокой доступности и отказоустойчивости.

Alertmanager может работать в кластерной конфигурации, умеет дедуплицировать данные с разных серверов Prometheus и может связываться с другими копиями Alertmanager, чтобы не отправлять несколько одинаковых оповещений. Поэтому можно установить по одной копии Alertmanager на оба сервера, которые мы продублировали: Prometheus A и Prometheus B. И не забываем про инструменты IaC и CM, чтобы управлять конфигурацией Alertmanager при помощи кода.

Экспортёры устанавливаются на конкретные системы-источники метрик, их дублировать не нужно. Единственное, что нужно сделать разрешить серверам Prometheus A и Prometheus B подключаться к ним.

С Pushgateway простым дублированием сервером не обойтись, потому что мы получим дуплицирование данных. В этом случае нам нужно иметь единую точку для приёма метрик. Для достижения высокой доступности и отказоустойчивости можно продублировать Pushgateway и настроить DNS Failover или балансировщик, чтобы при отказе одного сервера все запросы шли на другой (конфигурация active/passive). Таким образом у нас будет единая точка доступа для всех процессов, несмотря на наличие нескольких серверов.

Blackbox мы также можем продублировать для серверов Prometheus A и Prometheus B.

Итого, у нас есть два сервера Prometheus, две копии Alertmanager, связанные друг с другом, два Pushgateway в конфигурации active/passive и два Blackbox. Высокая доступность и отказоустойчивость достигнуты.

Нет особого смысла использовать только эти копии для сбора всех метрик сервиса. Сервис может быть расположен на нескольких VPC (Virtual Private Cloud), которые могут находиться в разных регионах, принадлежать разным аккаунтам и провайдерам. У вас даже могут быть собственные серверы. В этих случаях копии станут очень большими, а значит их станет сложнее чинить. Распространённая практика достижения высокой доступности и отказоустойчивости здесь иметь отдельный набор приложений для каждой части инфраструктуры. Принципы разделения инфраструктуры на части зависят от ваших потребностей, настроек сети и безопасности, доверия между командами и так далее.

В итоге мы имеем относительно небольшие копии Prometheus, продублированные вместе со всеми компонентами, упомянутыми выше. У нас есть код, который может их быстро воссоздать. И нам не страшен выход из строя одного компонента в каждой группе. Это определенно лучше плана "скрестить пальцы и надеяться, что ничего не упадёт".

VictoriaMetrics для долгосрочного хранения данных

Мы настроили Prometheus и его экосистему для достижения высокой доступности и отказоустойчивости. У нас есть несколько небольших групп Prometheus со связанными компонентами, каждая из которых решает задачи в своей части инфраструктуры. Это отлично работает для хранения данных в краткосрочном периоде. Для решения большинства задач нам достаточно хранения метрик в течение 10 дней. Что делать, если нужно хранить данные дольше? Например, когда требуется найти связь между разными периодами неделями или месяцами. Prometheus может работать с долгосрочными данными, но стоимость этого будет очень высокой из-за того, что инструменту требуется иметь к ним быстрый доступ.

Тут на помощь приходят Cortex, Thanos, M3DB, VictoriaMetrics и многие другие инструменты. Все они умеют собирать данные с нескольких серверов Prometheus, дедуплицировать их у нас точно будут дубликаты, так как каждый наш сервер существует в двух экземплярах, и предоставлять единое хранилище для собираемых метрик.

В этой статье я не буду сравнивать инструменты между собой, расскажу только про наш опыт работы с VictoriaMetrics.

Настройка кластерной версии

VictoriaMetrics доступен в двух версиях: обычная всё-в-одном (single-node version) и кластерная (cluster version). В обычной версии все компоненты объединены в одно приложение, поэтому инструмент проще настраивать, но масштабировать можно только вертикально. Кластерная версия разбита на отдельные компоненты, каждый из которых можно масштабировать вертикально и горизонтально.

Обычная версия хорошее и стабильное решение. Но мы любим всё усложнять (хех), поэтому выбрали кластерную версию.

Кластерная версия VictoriaMetrics состоит из трёх основных компонентов: vmstorage (хранение данных), vminsert (запись данных в хранилище) и vmselect (выборка данных из хранилища). В таком виде инструмент получается очень гибким, vminsert и vmselect выступают как своего рода прокси.

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

Самые важные параметры, которые нужно указать для vminsert это адреса хранилищ (storageNode) и количество хранилищ, на которые нужно реплицировать данные (replicationFactor=N, где N количество копий vmstorage). Но кто будет слать данные на балансировщик перед vminsert? Это будет делать Prometheus, если мы укажем адрес балансировщика в настройках remote_write.

vmstorage пожалуй, самый важный компонент VictoriaMetrics. В отличие от vminsert и vmselect, vmstorage имеет состояние (stateful), и каждая его копия ничего не знает о других копиях. Каждый запущенный vmstorage считает себя изолированным компонентом, он оптимизирован для облачных хранилищ с большим временем отклика (IO latency) и небольшим количеством операций в секунду (IOPS), что делает его существенно дешевле того способа хранения данных, который использует Prometheus.

Самые важные настройки vmstorage:

  • storageDataPath путь на диске, по которому будут храниться данные;

  • retentionPeriod срок хранения данных;

  • dedup.minScrapeInterval настройка дедупликации (считать дубликатами те записи, разница между временными метками которых меньше указанного значения).

У каждой копии vmstorage свои данные, но благодаря параметру replicationFactor, который мы указали для vminsert, одни и те же данные будут отсылаться в несколько (N) хранилищ.

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

vmselect отвечает за выборку данных из хранилищ. Его легко дублировать, перед созданными копиями тоже можно поставить балансировщик нагрузки, чтобы иметь один адрес для приёма запросов. Через этот балансировщик можно получить доступ ко всем данным, которые были собраны с нескольких групп Prometheus, и эти данные будут доступны столько времени, сколько вам нужно. Основным потребителем этих данных, скорее всего, будет Grafana. Как и vminsert, vmselect можно использовать при автоматическом масштабировании.

Настройка высокой доступности и отказоустойчивости для Grafana

Grafana умеет работать как с метриками, которые собирает Prometheus, так и с метриками, которые хранятся в VictoriaMetrics. Это возможно благодаря тому, что VictoriaMetrics поддерживает кроме собственного языка запросов (MetricsQL) ещё и PromQL, используемый Prometheus. Попробуем достичь высокой доступности и отказоустойчивости для Grafana.

По умолчанию Grafana использует SQLite для хранения состояния. SQLite удобен для разработки, отлично подходит для мобильных приложений, но не очень хорош для отказоустойчивости и высокой доступности. Для этих целей лучше использовать обычную СУБД. Например, мы можем развернуть PostgreSQL на Amazon RDS, который использует технологию Multi-AZ для обеспечения доступности, и это решит нашу главную проблему.

Для создания единой точки доступа мы можем запустить какое угодно количество копий Grafana и настроить их на использование одного и того же облачного PostgreSQL. Количество копий зависит от ваших потребностей, вы можете масштабировать Grafana горизонтально и вертикально. PostgreSQL можно установить и на серверы с Grafana, но нам лень это делать и больше нравится пользоваться услугами облачных провайдеров, когда они отлично справляются с задачей и не используют vendor lock. Это отличный пример того, как можно сделать жизнь проще.

Теперь нам нужен балансировщик нагрузки, который будет распределять трафик между копиями Grafana. Этот балансировщик мы дополнительно можем привязать к красивому домену.

Дальше остаётся соединить Grafana с VictoriaMetrics а точнее, с балансировщиком перед vmselect, указав Prometheus в качестве источника данных. На этом нашу инфраструктуру для мониторинга можно считать завершённой.

***

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

Это всё, что касается метрик. Нам ещё нужна система работы с логами, но это уже совсем другая история.

Список инструментов:

Оригинал статьи в англоязычном блоге Miro.

Подробнее..

Бесшовная миграция пользователей между доменами

01.07.2020 12:12:44 | Автор: admin
В начале 2019 года мы провели ребрендинг и поменяли название с RealtimeBoard на Miro. Следовательно, изменился домен сайта с realtimeboard.com на miro.com.

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

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

Передача данных и шифрование


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

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

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

Структура хэша:
url_encode(OpenSSL({  'token': 'токен',  'date': 'Дата шифрования',  'additional_values': ['param', 'param']}))

LocalStorage доступен только через JavaScript. Для решения этой задачи был выбран интерфейс postMessage и iframe, что позволяло безопасно отправлять кроссдоменные запросы. Данные в LocalStorage были сконвертированы в строки с помощью JSON.stringify() и переданы на домен miro.com, где они конвертировались обратно и записывались в LocalStorage домена miro.com.

Схема работы с описанием всех шагов



Схема в удобном для просмотра виде.

Пользователи могли попасть в сервис двумя путями: через старый домен realtimeboard.com (например, из закладок) или через новый miro.com (например, через рекламу).

Если пользователь заходил на старый домен:
  1. После открытия любой страницы realtimeboard.com выполняется шифрование токена пользователя, даты создания и дополнительной служебной информации.
  2. После шифрования происходит редирект на miro.com/migration/?data={hash} с передачей hash как Get параметр. Сам токен и служебная информация удаляются на старом домене, т.к они больше были не нужны.
  3. На новом домене miro.com выполняется расшифровка хэша, проверка на то, что он не просрочен, и установка токена пользователя, если он был передан.
  4. Проставляется флаг migrationToken, чтобы не выполнять миграцию снова.
  5. Для миграции LocalStorage мы проверяли наличие куки migrationLocalstorage. Если куки не существовало, выполнялся рендеринг страницы с JS-скриптом, который открывал в iFrame страницу relatimeboard.com/localstorage/ и принимал сообщения от неё с помощью postmessage После окончания миграции пользователя редиректило на страницу, которую он пытался открыть первый раз.

Если пользователь заходил сразу на новый домен miro.com, выполнялась проверка на прохождение пользователем миграции токена и LocalStorage. Если пользователь уже выполнил миграцию, то он оставался на домене miro.com. Если не выполнил происходил редирект на realtimeboard.com для получения токена (для этого мы хранили в куках два флага: migrationToken и migrationLocalstorage).

Эта схема так же хорошо работала для пользователей с SSO, которые выполнили авторизацию на старом домене realtimeboard.com. Был добавлен список маршрутов, которые работали без миграции токена на новый домен. В них входил маршрут для SSO, который выполнялся как обычно и выполнял редирект на /app/ или /login/ в зависимости от состояния своей работы, после чего снова подключался механизм миграции токена.

Вывод


На исследование и подготовку я потратил месяц, ещё месяц на обкатку (параллельно занимался другими проектами). Благодаря этому решению мы смогли мигрировать авторизованных пользователей со старого домена на новый и поддержать пользователей, которые использовали SSO для авторизации через старый домен.
Подробнее..

Выбор хэш-функции в задаче шардирования данных

28.12.2020 02:07:52 | Автор: admin

Введение

Мы в Miro работаем над процессом шардирования баз Postgres и используем разные подходы в зависимости от бизнес-требований. Недавно перед нами встала задача шардирования новых баз, в ходе неё мы выбрали новый для нас подход к шардированию, основанный на согласованном хешировании (consistent hashing).

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

Oб архитектурном подходе

Есть много продуктов (mongo, redis, и т.д.), использующих согласованное хеширование для шардинга, и наша реализация будет сильно похожа на них.

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

Плюсами данного подхода являются:

  • равномерное распределение сущностей по шардам;

  • определение соответствия сущностей и шардов без дополнительного хранилища с минимум ресурсо-затрат;

  • возможность добавления новых шардов в кластер.

Из минусов:

  • неэффективность некоторых операций поиска, в которых необходимо делать запросы на все шарды;

  • достаточно сложный процесс решардинга.

Требования

Центральным местом решения является выбор java-реализации хэш-функции.

Функция принимает на вход ключ - объект строки, размером до 256 символов, и выдает хэш-код - беззнаковое целое число, размером до 4 байт. На самом деле мы будем сравнивать реализации которые генерируют хэш-коды размером 2 и 4 байта.

Критерии сравнения

Рассмотрим четыре распространенных критерия сравнения реализаций хэш-функций:

  1. Скорость, функция должна работать быстро, на любых входных данных;

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

  3. Устойчивость к коллизиям (первого и второго рода);

  4. Соответствие лавинному эффекту. Отражает зависимость всех выходных битов от каждого входного бита, на любых входных данных.

Для нашей задачи нам будут важны только первые два критерия: первый - поскольку операция расчета хэша будет очень частой; и второй - поскольку крайне важно, чтобы данные распределялись по шардам равномерно.

Отсутствие возможности атаки на характеристики функции делает для нас неважным третий критерий.

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

Реализации

Мы будем рассматривать самые популярные java-реализации не-криптографических хэш-функций:

  1. DJB2 (32-бита);

  2. SDBM (32-бита);

  3. LoseLose (32-бита);

  4. FNV-1 / FNV-1a (32-бита);

  5. CRC16 (16-бит) ;

  6. Murmur2/Murmur3 (32-бита).

Тестирование

Входные данные

В качестве входных данных мы будем использовать следующие наборы данных

  1. Набор реальных данных, составленный из 216,553 английских слов;

  2. Набор синтетических данных, составленный из рандомно сгенерированных символов в кодировке UTF-8.

В обоих тестовых наборах мы будем иметь группы строк с определенными длинами (кол-во символов) - "2", "4", "8", "16", "32", "64", "128", "256".

Метрики

Для сравнения различных критериев мы будем использовать следующие метрики:

  1. Для первого критерия, скорости - ops/ms (кол-во операций в миллисекунду работы);

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

Инструменты

Оценка скорости работы

Для оценки скорости работы мы воспользуемся нагрузочными тестами и библиотекой JMH. Общая схема тестовой итерации выглядит следующим образом:

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

Для бэнчмарков мы будем использовать следующие настройки

  • Кол-во warmup-итераций - 50;

  • Кол-во measurement-итераций - 100;

  • Режим - throughput

  • Добавим ограничение по памяти -Xms1G, -Xmx8G

  • Для оценки расхода памяти добавим GCProfiler

Полный код тестов можно посмотреть здесь.

Оценка распределения результатов

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

Алгоритм для проверки гипотезы следующий:

  1. Разобьем выборку на частичные интервалы, число которых найдем по формуле Стерджеса, а их длину найдем по правилу равноинтервальной группировки;

  2. Для каждого интервала подсчитаем его характеристики - среднее значение, частоты, относительные частоты;

  3. Подсчитаем выборочное среднее \overline{x_{b}} , среднеквадратическое отклонение \sigma_{b} = \sqrt{D_{b}} и теоретические частоты

    \hat{n_{i}}=np_{i},

    где n число элементов в выборке, а p_{i} вероятность попадания случайной величины в частичные интервалы, в нашем случае она равна -

    p_{i} = \frac{x_{length}}{b - a},

    где x_{length} - одинаковая длина интервалов, a параметры a и b - a = \overline{x_{b}} - \sqrt{3\sigma_{b}} , b = \overline{x_{b}} + \sqrt{3\sigma_{b}} ;

  4. Можем приступить к расчёту критерия согласия, по формуле

    \chi_{набл}^2 = \sum\frac{n_{i}-\hat{n_{i}}}{\hat{n_{i}}},

    где n_{i} - эмпирические частоты, полученные из выборки, \hat{n_{i}} - теоретические частоты, найденные по формулам выше;

  5. Определяем по таблице критических точек распределения \chi_{кр}^2(\alpha, k) , по заданному уровню значимости и числу степеней свободы k ;

  6. Если \chi_{набл}^2<\chi_{кр}^2 , то принимаем гипотезу, если же данное условие не выполняется отвергаем.

Код для расчёта критерия согласия и вероятностных характеристик выборок здесь.

Общая схема тестовой итерации похожа на схему в предыдущем разделе и выглядит следующим образом:

Слова из каждого тестового набора мы сгруппируем по длине, при максимальном значении символов в 256. Затем создадим входные тестовые выборки разных размеров в диапазоне 16384, 8192, 4096, 2048, 1024, в выборки поместим слова из каждой группы, с одинаковой вероятностью.

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

Полный код тестов можно посмотреть здесь.

Результаты

Оценка скорости работы

Рассмотрим скорость работы (количество операций в миллисекунду) для различных имплементаций в зависимости от длины входных строк.

В диапазоне от двух до восьми символов:

ДиаграммаДиаграмма

Видно, что в этом диапазоне практически все алгоритмы работают с одинаковой скоростью, незначительно опережает всех loseLose, а очевидными аутсайдерами выглядят только crc16 и sdbm.

В диапазоне от 16 до 256 символов:

ДиаграммаДиаграмма

Функция murmur2 явный фаворит, ей немного уступает murmur; crc16 и sdbm остались в аутсайдерах и на этой выборке.

Оценка распределения результатов

Рассмотрим таблицу результатов соответствия критерию Пирсона

Видно, что имплементации crc16, murmur2, murmur3 удовлетворяют критерию Пирсона о равномерном распределении практически на всех выборках.

Рассмотрим гистограммы относительных частот, в разрезе разных выборок.

На гистограммах ниже, для loseLose, Djb2, Sdbm, не прошедших тест, видно, что распределение далеко от равномерного и больше похоже на геометрическое:

ДиаграммаДиаграммаДиаграммаДиаграммаДиаграммаДиаграмма

Для проваливших тест Fnv1 и Fnv1a ситуация похожа, распределения отдалённо напоминают нормальное:

.

ДиаграммаДиаграммаДиаграммаДиаграмма

Смотрим на тройку победителей:

ДиаграммаДиаграммаДиаграммаДиаграммаДиаграммаДиаграмма

За исключением некоторых всплесков, crc16, murmur2, murmur3 удовлетворяют критерию Пирсона, что согласуется с характеристиками их гистограмм относительных частот.

Выводы

Рассмотрим выбор наиболее подходящей реализации, которую мы оцениваем по двум выбранным критериям: скорость работы и удовлетворение гипотезы о равномерном распределении.

Скорость работы. Функции murmur2/murmur3 имеют лучшее время работы для входных строк длиной больше 8 символов.

Удовлетворение гипотезы о равномерном распределении. Можем выделить три функции, для которых гипотеза принимается для большинства наборов данных: crc16, murmur2/murmur3. Графики распределения гистограмм относительных частот подтверждают вид равномерного распределения для функций crc16, murmur2/murmur3.

Таким образом, исходя из двух критериев, лучшим выбором являются реализации murmur2/murmur3.

Подробнее..

Митап Инженер заходит в бар Dev-to-Teamlead

28.05.2021 08:05:31 | Автор: admin

3июня, 15:00 МСК, онлайн.
Регистрация https://miro-event.timepad.ru/event/1650491/

Эксперты

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

Тема

В рамках серии митапов "Инженер заходит в бар" продолжаем исследовать траектории профессионального развития разработчиков. На этот раз поговорим про переход из инженера в руководителя команды.

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

Ты соглашаешься и начинается: проводишь больше времени на митингах, чем за написанием кода; нужно делатьperformance review, 1-to-1 и прочую психологию с социологией; мысленно примеряешь на себя стереотип "Был хороший разработчик, стал плохой руководитель".

На митапе поговорим с ребятами, кто был в такой ситуации и как-то справился.

Обсудим:

  • момент перехода что было самое сложное и что помогло преодолеть;

  • отложенные сложности какие были проблемы по мере накопления тимлидского опыта;

  • следующие шаги после тимлидства кто куда пошёл дальше, почему именно туда.

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

Серия "Инженер заходит в бар"

Мы делаем митапы для тех, кто умеет программировать и столкнулся с осознанием, что IT не только про программирование. Кроме кода есть люди, с которыми нужно договариваться. Есть сверхскоростные темпы изменения технологий и рынка. Нужно постоянно учиться новому. Есть желание делать нужное. "Инженер заходит в бар" митапы для тех, кто задумывается о процессах в IT, независимо от своей роли.

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

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

Все выпуски серии.

Организаторы

Мы инженеры изMiroи DevRel-бюро Долгушев и Сторожилов. Мы много общаемся с разными IT-компаниями и видим, насколько по-разному все отвечают на схожие вопросы. Нам интересно разобраться, увидеть систему (или её отсутствие) и обсудить всё это с другими инженерами. Ну и просто поговорить с интересными людьми об их опыте.

Регистрация https://miro-event.timepad.ru/event/1650491/

Подробнее..

Как не продолбать архитектуру в погоне за фичами

01.02.2021 08:17:17 | Автор: admin

Я работаю в Miro со дня основания, вначале как фронтенд инженер, сейчас как менеджер core-команд, которые разрабатывают внутреннее ядро канваса и realtime-коллаборации на нём.

Мы очень быстро растём: в пользователях, в размере команды, в количестве выпускаемых фич. Немного фактов за 2020 для контекста:

  • Перешагнули рубеж в 10 миллионов регистраций;

  • Пиковая онлайн-нагрузка за год выросла в 7 раз;

  • Команда разработки выросла в 2 раза (инженеры, продакты, дизайнеры);

Выглядит круто! Но есть нюансы.

Наблюдая за компанией 9 лет, я вижу, что кратное увеличение количества инженеров приводит к падению скорости разработки. Задачи по созданию новой функциональности приводят к костылям. Текущая архитектура не позволяет сделать их правильно, а на рефакторинг времени не хватает. Непонятно кто именно должен брать на себя рефакторинг. Начинаемые архитектурные изменения не доводятся до конца из-за отсутствия фокуса. Порог входа в код повышается, онбордить новичков становится сложнее. Time to market новых фичей падает.

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

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

Выделим 20% времени на рефакторинг и улучшения

И так, ситуация X времени назад. У нас есть несколько фичевых команд, которые разрабатывают новую функциональность. Команды самостоятельно реализуют фичу от начала до конца и могут менять любой необходимый для этого код. У каждой команды свой продакт-менеджер, он управляет фокусом и бэклогом команды. Цель продакт-менеджера достигать определённых бизнес-метрик.

Между продакт-менеджером и инженерами часто возникает типичный конфликт качественно vs быстро.

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

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

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

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

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

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

Закрепим ответственность за кодом за конкретными командами

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

Что значит владеть кодом:

  • Ревьюить пул-реквесты на любые изменения;

  • Согласовывать технические решения от других команд по большим изменениям;

  • Помогать и консультировать другие команды;

  • Приводить код в соответствие критериям качества, принятым в компании, например, иметь тесты и документацию на код;

  • И, разумеется, исправлять ошибки на проде.

Как это выглядит с технической точки зрения.

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

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

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

Что делать? Создадим новый тип команд core-команды, которые будут отвечать за развитие фундамента.

Вынесем фокус на архитектуру и системные задачи в отдельные core-команды

Это позволит не смешивать фичевые и системные беклоги в одной команде. Таким образом, у core-команд будет постоянный фокус на развитии архитектуры продукта, а беклогом этих команд будет управлять техлид или архитектор.

Цель core-команд развитие внутренней платформы, на базе которой остальные команды смогут быстрее и качественнее строить новую функциональность и развивать существующую.

Примеры задач core-команды:

  • Упрощение доменной модели данных и предоставление API для неё;

  • Создание внутреннего фреймворка для фичевых команд;

  • Изоляция слоёв приложения;

  • Стабильность и производительность сервиса;

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

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

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

Окей. Создали команды. Будет ли этого достаточно или что-то может пойти не так?

Разумеется может.

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

Есть много способов уйти не туда. Вопрос как сфокусироваться на важном?

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

Создадим техническую стратегию

Стратегия должна помочь core-командам понять, что на самом деле мы собираемся построить не через месяц, а через несколько лет. Не так важно, как это называть: техническая стратегия, архитектурное видение, Painted Picture, просто план, важнее это формализовать, чтобы перенести идеи из головы в документ и договориться, что все согласны со стратегией и понимают её одинаково.

Техническая стратегия включает три источника требований:

  • Бизнес: видение развития компании, продуктовая стратегия, крупные фичи, которые мы планируем реализовать в будущем, но не можем из-за блокеров в архитектуре;

  • Надёжность и производительность: какой ожидается рост нагрузки, под что стоит оптимизировать производительность, какие метрики для нас приоритетные;

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

Первый черновик стратегии мы получили, запершись на несколько дней в переговорке с наиболее опытными разработчиками, и после нескольких мозговых штурмов создали договорились о следующем:

  • Целевое разбитие на компоненты и слои из которых должно состоять приложения.

  • Зоны ответственности команд: явно договорились, какие команды за какие слои и компоненты отвечают;

  • Целевая модель данных: объекты в системе и взаимосвязи между ними;

  • Перечень крупных изменений в core-компонентах, которые ускорят или упростят разработку, а также разлочат продуктовые фичи.

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

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

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

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

Итого

Шаги, которые могут помочь сохранить нужный уровень гибкости и скорости разработки новой функциональности, при условии быстрого роста продукта и размеров команды:

  • Дать фичевым командам выделенное время на работу с техническим долгом;

  • Договориться о политиках владения кодом и превратить это в процесс;

  • Определить долгосрочную продуктовую стратегию и требования отбизнеса: понимаем, насколько текущая архитектура удовлетворяет этим требованиям. Если не удовлетворяет и инвестиций надо много,т.е. изменения не реализовать силами текущих команд в проектном режиме, то создаём отдельные команды из ребят с хорошим знанием продукта. И эти команды фокусируем на приведение архитектуры к целевой;

  • Целевую архитектуру вырабатываем вместе с бизнесом, фиксируем описание, планируем реализацию;

  • Далее фигачим, регулярно актуализируем видение, не забывая рассказывать о ключевых изменениях всем командам.

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

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

Подробнее..

SQL миграции в Postgres. Часть 1

03.02.2021 00:17:16 | Автор: admin

Как обновить значение атрибута для всех записей таблицы? Как добавить первичный или уникальный ключ в таблицу? Как разбить таблицу на две? Как ...

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

На эти и другие вопросы, возникающие при проведении миграций схемы и данных в PostgreSQL, постараемся дать ответы в виде практических советов.

Эта статья расшифровка выступления на конференции SmartDataConf (здесь можно найти презентацию, со временем появится видео). Текста получилось много, поэтому материал будет разбит на 2 статьи:

  • базовые миграции
  • подходы по обновлению больших таблиц.

В конце есть выжимка всей статьи в виде сводной таблицы-шпаргалки.

Содержание


Суть проблемы
Добавление столбца
Добавление столбца со значением по умолчанию
Удаление столбца
Создание индекса
Создание индекса для партиционированной таблицы
Создание ограничения NOT NULL
Создание внешнего ключа
Создание ограничения уникальности
Создание первичного ключа
Краткая шпаргалка с миграциями

Суть проблемы


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


При такой схеме обновления приложения зачастую происходят с простоем (downtime). В это же время можно обновлять БД. В такой ситуации главный критерий это время, то есть нужно выполнить миграцию как можно быстрее, чтобы минимизировать время недоступности сервиса.

Если приложение растет и появляется необходимость проводить релизы без простоя, мы начинаем использовать несколько серверов приложений. Их может быть сколько угодно, и они будут на разных версиях. В этом случае появляется необходимость обеспечения обратной совместимости.


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


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

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

Добавление столбца


ALTER TABLE my_table ADD COLUMN new_column INTEGER -- быстро и безопасно

Наверное, любой человек, который работает с БД, писал подобную миграцию. Если говорить про PostgreSQL, то такая миграция весьма дешевая и безопасная. Сама команда, хоть и захватывает блокировку самого высокого уровня (AccessExclusive), выполняется очень быстро, поскольку под капотом происходит лишь добавление метаинформации о новом столбце без перезаписи данных самой таблицы. В большинстве случаев это происходит незаметно. Но проблемы могут возникнуть, если в момент миграции есть долгие транзакции, работающие с этой таблицей. Чтобы понять суть проблемы рассмотрим на небольшом примере как упрощенно работают блокировки в PostgreSQL. Этот аспект будет очень важен при рассмотрении большинства других миграций в том числе.

Предположим, у нас есть большая таблица, и мы делаем из нее SELECT всех данных. В зависимости от размера БД и самой таблицы он может длиться несколько секунд или даже минут.


На время выполнения транзакции захватывается самая слабая блокировка AccessShare, которая защищает от изменений структуры таблицы.

В этот момент приходит другая транзакция, которая как раз таки пытается сделать запрос ALTER TABLE к этой таблице. Команда ALTER TABLE, как уже было сказано ранее, захватывает блокировку AccessExclusive, которая не совместима вообще ни с какой другой. Она встает в очередь.

Эта очередь блокировок разгребается в строгом порядке, т.е. даже если после ALTER TABLE приходят другие запросы (например, также SELECTы), которые сами по себе не конфликтуют с первым запросом, они все встают в очередь за ALTER TABLE. В итоге приложение встало и ждет, пока ALTER TABLE выполнится.

Что делать в такой ситуации? Можно ограничить время захвата блокировки с помощью команды SET lock_timeout. Выполняем эту команду перед ALTER TABLE (ключевое слово LOCAL означает, что настройка действует только в пределах текущей транзакции, иначе в пределах текущей сессии):

SET LOCAL lock_timeout TO '100ms'

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

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

Добавление столбца со значением по умолчанию


-- быстро и безопасно с PG 11ALTER TABLE my_table ADD COLUMN new_column INTEGER DEFAULT 42

Если эта команда выполняется в PostgreSQL старой версии (ниже 11), то это приводит к перезаписи всех строк в таблице. Очевидно, что если таблица большая, то это может занять много времени. А поскольку на время выполнения захватывается строгая блокировка (AccessExclusive), то все запросы к таблице также блокируются.

Если версия PostgreSQL 11 или свежее, эта операция обходится весьма дешево. Дело в том, что в 11й версии была сделана оптимизация, благодаря которой вместо перезаписи таблицы значение по умолчанию сохраняется в специальную таблицу pg_attribute, и в дальнейшем при выполнении SELECT, все пустые значения этого столбца будут на лету заменяться на это значение. При этом впоследствии, когда будет происходить перезапись строк в таблице из-за других модификаций, значение будет записываться в эти строки.

Более того, с 11й версии также можно сразу создавать новый столбец и помечать его как NOT NULL:

-- быстро и безопасно с PG 11ALTER TABLE my_table ADD COLUMN new_column INTEGER DEFAULT 42 NOT NULL

Как быть, если PostgreSQL старше, чем 11?

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

ALTER TABLE my_table ADD COLUMN new_column INTEGER;ALTER TABLE my_table ALTER COLUMN new_column SET DEFAULT 42;

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

Таким образом после выполнения этих команд, нам остается обновить значения, которые уже были в таблице. Грубо говоря, нам нужно сделать примерно вот так:

UPDATE my_table set new_column = 42 -- небезопасно на большой таблице

Но такой UPDATE в лоб делать на самом деле нельзя, поскольку при обновлении большой таблицы мы надолго заблокируем всю таблицу. Во второй статье (здесь в будущем будет ссылка) мы рассмотрим, какие существуют стратегии для обновления больших таблиц в PostgreSQL, а пока будем предполагать, что мы каким-то образом обновили данные, и теперь и старые данные, и новые будут с необходимым значением по умолчанию.

Удаление столбца


ALTER TABLE my_table DROP COLUMN new_column -- быстро и безопасно

Здесь логика такая же, как и при добавлении столбца: данные таблицы не модифицируются, происходит только изменение метаинформации. В данном случае столбец помечается как удаленный и недоступный при запросах. Это объясняет тот факт, что при удалении столбца в PostgreSQL физически место не освобождается (если не выполнять VACUUM FULL), то есть данные старых записей по-прежнему остаются в таблице, но недоступны при обращении. Освобождение происходит постепенно при перезаписи строк в таблице.

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

  • Для начала необходимо убрать все ограничения (NOT NULL, CHECK, ...), которые есть на этом столбце:
    ALTER TABLE my_table ALTER COLUMN new_column DROP NOT NULL
    
  • Следующий шаг обеспечить совместимость бэкенда. Нужно убедиться, что столбец нигде не используется. Например, в Hibernate необходимо пометить поле с помощью аннотации @Transient. В JOOQ, который мы используем, поле добавляется в исключения с помощью тэга <excludes>:
    <excludes>my_table.new_column</excludes>
    

    Также нужно внимательно посмотреть на запросы "SELECT *" фреймворки могут мапить все столбцы в структуру в коде (и наоборот) и, соответственно, снова можно столкнуться с проблемой обращения к несуществующему столбцу.

После вывода изменений на все сервера приложений можно удалить столбец.

Создание индекса


CREATE INDEX my_table_index ON my_table (name) -- небезопасно, блокировка таблицы

Те, кто работает с PostgreSQL, наверное, знают, что такая команда блокирует всю таблицу. Но еще с очень старой версии 8.2 существует ключевое слово CONCURRENTLY, которое позволяет создавать индекс в неблокирующем режиме.

CREATE CONCURRENTLY INDEX my_table_index ON my_table (name) -- безопасно

Команда работает медленнее, но не мешает параллельным запросам.

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

SELECT pg_index.indisvalid    FROM pg_class, pg_indexWHERE pg_index.indexrelid = pg_class.oid    AND pg_class.relname = 'my_table_index'

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

DROP INDEX CONCURRENTLY my_table_indexUPDATE my_table ...CREATE CONCURRENTLY INDEX my_table_index ON my_table (name)

Важно заметить, что команда REINDEX, которая как раз предназначена для пересоздания индекса, до 12й версии работает только в блокирующем режиме, что не дает возможности ее использовать. В 12й версии PostgreSQL появилась поддержка CONCURRENTLY, и теперь и ей можно пользоваться.

REINDEX INDEX CONCURRENTLY my_table_index -- с PG 12

Создание индекса для партиционированной таблицы


Отдельно стоит обсудить создание индексов для партиционированных таблиц. В PostgreSQL существует 2 вида партиционирования: через наследование и декларативное, появившееся в 10й версии. Рассмотрим оба на простом примере.

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

При партиционировании через наследование мы будем иметь примерно следующую схему.

Родительская таблица:

CREATE TABLE my_table (...reg_date  date not null)

Дочерние партиции для 2020 и 2021 годов:

CREATE TABLE my_table_y2020 (CHECK ( reg_date >= DATE '2020-01-01' AND reg_date < DATE '2021-01-01' ))INHERITS (my_table);CREATE TABLE my_table_y2021 (CHECK ( reg_date >= DATE '2021-01-01' AND reg_date < DATE '2022-01-01' ))INHERITS (my_table);

Индексы по полю партиционирования для каждой из партиций:

CREATE INDEX ON my_table_y2020 (reg_date);CREATE INDEX ON my_table_y2021 (reg_date);

Создание триггера/правила для вставки данных в таблицу оставим за рамками.

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

CREATE CONCURRENTLY INDEX my_table_y2020_index ON my_table_y2020 (name);CREATE CONCURRENTLY INDEX my_table_y2021_index ON my_table_y2021 (name);

Теперь рассмотрим декларативное партиционирование.

CREATE TABLE my_table (...) PARTITION BY RANGE (reg_date);CREATE TABLE my_table_y2020 PARTITION OF my_table FOR VALUES FROM ('2020-01-01') TO ('2020-12-31');CREATE TABLE my_table_y2021 PARTITION OF my_table FOR VALUES FROM ('2021-01-01') TO ('2021-12-31');

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

В 11й версии декларативное партиционирование было доработано и теперь таблицы обслуживаются вместе. Создание индекса на родительской таблице автоматически приводит к созданию индексов для всех существующих и новых партиций, которые будут созданы в будущем:

-- с PG 11 удобно для новой (пустой) партиционированной таблицыCREATE INDEX ON my_table (reg_date)

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

CREATE INDEX ON my_table (name) -- блокировка таблиц

К сожалению, CREATE INDEX не поддерживает ключевое слово CONCURRENTLY для партиционированных таблиц. Чтобы обойти ограничение и провести миграцию без блокировок можно сделать следующее.

  1. Создать индекс для родительской таблицы с опцией ONLY
    CREATE INDEX my_table_index ON ONLY my_table (name)
    

    Команда создаст пустой невалидный индекс без создания индексов для партиций.
  2. Создать индексы для каждой из партиций:
    CREATE CONCURRENTLY INDEX my_table_y2020_index ON my_table_y2020 (name);CREATE CONCURRENTLY INDEX my_table_y2021_index ON my_table_y2021 (name);
    
  3. Прикрепить индексы партиций к индексу родительской таблицы:
    ALTER INDEX my_table_index ATTACH PARTITION my_table_y2020_index;ALTER INDEX my_table_index ATTACH PARTITION my_table_y2021_index;
    
    Как только все индексы будут прикреплены, индекс родительской таблицы автоматически станет валидным.

Ограничения


Теперь пройдемся по ограничениям: NOT NULL, внешние, уникальные и первичные ключи.

Создание ограничения NOT NULL


ALTER TABLE my_table ALTER COLUMN name SET NOT NULL -- блокировка таблицы

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

Что можно сделать? В PostgreSQL есть другой тип ограничения, CHECK, с помощью которого можно получить желаемый результат. Это ограничение проверяет любое булево условие, состоящее из столбцов строки. В нашем случае условие тривиально CHECK (name IS NOT NULL). Но самое важное то, что ограничение CHECK поддерживает невалидность (ключевое слово NOT VALID):

ALTER TABLE my_table ADD CONSTRAINT chk_name_not_null     CHECK (name IS NOT NULL) NOT VALID -- безопасно, с PG 9.2

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

Для обеспечения гарантии, что существующие записи также удовлетворяют ограничению необходимо провести его валидацию (конечно же, предварительно обновив данные в таблице):

ALTER TABLE my_table VALIDATE CONSTRAINT chk_name_not_null

Команда итерируется по строкам таблицы и проверяет, что все записи не not null. Но в отличие от обычного NOT NULL ограничения, блокировка, захватываемая в этой команде, не такая строгая (ShareUpdateExclusive) она не блокирует операции insert, update и delete.

Создание внешнего ключа


ALTER TABLE my_table ADD CONSTRAINT fk_group     FOREIGN KEY (group_id) REFERENCES groups(id) -- блокировка обеих таблиц

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

К счастью, внешние ключи в PostgreSQL также поддерживают NOT VALID, а это значит мы можем использовать тот же подход, что был рассмотрен ранее с CHECK. Создаем невалидный внешний ключ:

ALTER TABLE my_table ADD CONSTRAINT fk_group     FOREIGN KEY (group_id)REFERENCES groups(id) NOT VALID

затем обновляем данные и проводим валидацию:

ALTER TABLE my_table VALIDATE CONSTRAINT fk_group_id


Создание ограничения уникальности


ALTER TABLE my_table ADD CONSTRAINT uk_my_table UNIQUE (id) -- блокировка таблицы

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

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

SELECT conindid index_oid, conindid::regclass index_name     FROM pg_constraint WHERE conname = 'uk_my_table_id'

При этом большая часть времени создания ограничения как раз таки уходит на индекс, а его последующая привязка к ограничению происходит очень быстро. Более того, если у вас уже есть созданный уникальный индекс, то такую привязку можно осуществить самостоятельно, создавая индекс с помощью ключевых слов USING INDEX:

ALTER TABLE my_table ADD CONSTRAINT uk_my_table_id UNIQUE     USING INDEX uk_my_table_id -- быстро, с PG 9.1

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

В этот момент может возникнуть вопрос зачем вообще создавать ограничение, если индекс выполняет ровно то, что требуется гарантирует уникальность значений? Если исключить из сравнения partial индексы, то с функциональной точки зрения результат действительно почти идентичен. Единственное отличие, которое удалось найти, состоит в том, что ограничения могут быть отложенными (deferrable), а индексы нет. В документации к старым версиям PostgreSQL (до 9.4 включительно) была сноска с информацией о том, что предпочтительный способ создания ограничения уникальности это явное создание ограничения ALTER TABLE ... ADD CONSTRAINT, а использование индексов стоит рассматривать как деталь реализации. Однако, в более свежих версиях эту сноску удалили.

Создание первичного ключа


Первичный ключ помимо уникальности накладывает ограничение not null. Если столбец изначально имел такое ограничение, то превратить его в первичный ключ не составит труда также создаем уникальный индекс CONCURRENTLY, а затем первичный ключ:

ALTER TABLE my_table ADD CONSTRAINT uk_my_table_id PRIMARY KEY     USING INDEX uk_my_table_id -- если id is NOT NULL

Важно отметить, что столбец должен иметь честное ограничение NOT NULL рассмотренный ранее подход с помощью CHECK не сработает.

Если же ограничения нет, то до 11-й версии PostgreSQL ничего не поделать без блокировки первичный ключ никак не создать.

Если у вас 11-й PostgreSQL или свежее, эту задачу можно решить путем создания нового столбца, который будет заменять существующий. Итак, по шагам.

Создаем новый столбец, который по умолчанию not null и имеет значение по умолчанию:

ALTER TABLE my_table ADD COLUMN new_id INTEGER NOT NULL DEFAULT -1 -- безопасно с PG 11

Настраиваем синхронизацию данных старого и нового столбцов с помощью триггера:

CREATE FUNCTION on_insert_or_update() RETURNS TRIGGER AS$$BEGIN  NEW.new_id = NEW.id;  RETURN NEW;END;$$ LANGUAGE plpgsql;CREATE TRIGGER trg BEFORE INSERT OR UPDATE ON my_tableFOR EACH ROW EXECUTE PROCEDURE on_insert_or_update();

Далее необходимо обновить данные строк, которые не затронулись триггером:

UPDATE my_table SET new_id = id WHERE new_id = -1 -- не делать на большой таблице

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

ALTER TABLE my_table RENAME COLUMN id TO old_id;ALTER TABLE my_table RENAME COLUMN new_id TO id;ALTER TABLE my_table RENAME COLUMN old_id TO new_id;

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

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

Краткая шпаргалка с миграциями


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

SET LOCAL lock_timeout TO '100ms'

Миграция Рекомендуемый подход
Добавление столбца
ALTER TABLE my_table ADD COLUMN new_column INTEGER
Добавление столбца со значением по умолчанию [и NOT NULL] c PostgreSQL 11:
ALTER TABLE my_table ADD COLUMN new_column INTEGER DEFAULT 42 [NOT NULL]

до PostgreSQL 11:
  1. ALTER TABLE my_table ADD COLUMN new_column INTEGER;ALTER TABLE my_table ALTER COLUMN new_column SET DEFAULT 42;
    
  2. обновление таблицы
Удаление столбца
  1. удаление ограничений (NOT NULL, CHECK и т.д.)
  2. подготовка кода
  3. ALTER TABLE my_table DROP COLUMN removed_column
    
Создание индекса
CREATE CONCURRENTLY INDEX my_table_index ON my_table (name)

Если завершилось ошибкой:
  1. DROP INDEX CONCURRENTLY my_table_index
    
  2. обновление таблицы
  3. CREATE CONCURRENTLY INDEX my_table_index ON my_table (name)
    

Создание индекса для партиционированной таблицы Партиционирование через наследование + декларативное в PG 10:
CREATE CONCURRENTLY INDEX my_table_y2020_index ON my_table_y2020 (name);CREATE CONCURRENTLY INDEX my_table_y2021_index ON my_table_y2021 (name);...

Декларативное партиционирование с PG 11:
  1. CREATE INDEX my_table_index ON ONLY my_table (name)
    
  2. CREATE CONCURRENTLY INDEX my_table_y2020_index ON my_table_y2020 (name);CREATE CONCURRENTLY INDEX my_table_y2021_index ON my_table_y2021 (name);...
    
  3. ALTER INDEX my_table_index ATTACH PARTITION my_table_y2020_index;ALTER INDEX my_table_index ATTACH PARTITION my_table_y2021_index;...
    
Создание ограничения NOT NULL
  1. ALTER TABLE my_table ADD CONSTRAINT chk_name_not_null CHECK (name IS NOT NULL) NOT VALID
    
  2. обновление таблицы
  3. ALTER TABLE my_table VALIDATE CONSTRAINT chk_name_not_null
    

Создание внешнего ключа
  1. ALTER TABLE my_table ADD CONSTRAINT fk_group FOREIGN KEY (group_id) REFERENCES groups(id) NOT VALID
    
  2. обновление таблицы
  3. ALTER TABLE my_table VALIDATE CONSTRAINT fk_group_id
    
Создание ограничения уникальности
  1. CREATE UNIQUE INDEX CONCURRENTLY uk_my_table_id ON my_table(id)
    
  2. ALTER TABLE my_table ADD CONSTRAINT uk_my_table_id UNIQUE USING INDEX uk_my_table_id
    
Создание первичного ключа Если столбец IS NOT NULL:
  1. CREATE UNIQUE INDEX CONCURRENTLY uk_my_table_id ON my_table(id)
    
  2. ALTER TABLE my_table ADD CONSTRAINT uk_my_table_id PRIMARY KEY USING INDEX uk_my_table_id
    

Если столбец IS NULL c PG 11:
  1. ALTER TABLE my_table ADD COLUMN new_id INTEGER NOT NULL DEFAULT -1
    
  2. CREATE FUNCTION on_insert_or_update() RETURNS TRIGGER AS$$BEGINNEW.new_id = NEW.id;RETURN NEW;END;$$ LANGUAGE plpgsql;CREATE TRIGGER trg BEFORE INSERT OR UPDATE ON my_tableFOR EACH ROW EXECUTE PROCEDURE on_insert_or_update();
    
  3. обновление таблицы
  4. ALTER TABLE my_table RENAME COLUMN id TO old_id;ALTER TABLE my_table RENAME COLUMN new_id TO id;ALTER TABLE my_table RENAME COLUMN old_id TO new_id;
    
  5. CREATE UNIQUE INDEX CONCURRENTLY uk_my_table_id ON my_table(id)
    
  6. ALTER TABLE my_table ADD CONSTRAINT uk_my_table_id PRIMARY KEY USING INDEX uk_my_table_id
    
  7. DROP TRIGGER trg ON my_table;DROP FUNCTION on_insert_or_update();ALTER TABLE my_table DROP COLUMN new_id;
    

В следующей статье рассмотрим подходы по обновлению больших таблиц.
Всем легких миграций!
Подробнее..

Токсичность в команде, компании и индустрии. Конспект митапа из серии Инженер заходит в бар

18.09.2020 16:04:09 | Автор: admin
Собрали инсайты митапа на тему токсичности в общении. Дискуссия состоялась между инженерами и техлидами из Miro, Parma TG, Xsolla и SEMrush.

Митап прошёл в рамках серии Инженер заходит в бар, где инженеры из разных IT-компаний общаются на профессиональные не-инженерные темы. Серия мероприятий организована инженерами из компании Miro, при поддержке DevRel-бюро Долгушев и Сторожилов.



Третий митап серии состоится 24 сентября. Тема как бизнес и инженеры общаются и договариваются друг с другом. Спикеры CTO и тимлиды из Miro, Яндекс.Практикум, LANIT, Алгоритмика. Регистрация.

Оглавление:


  • Что такое токсичность? Примеры
  • Тактика: как работать с токсичностью
  • Low social skill и корреляция с токсичностью
  • Токсичность незнакомых людей в офлайне, онлайне. Как это работает?
  • Что делать, если люди считают, что токсичный ты сам?

Что такое токсичность? Примеры


Токсичный (toxic) слово 2018 года по версии Оксфордского cловаря. Помимо прямого значения, слово токсичный можно использовать для описания обстановки на рабочем месте, культуры, отношений или характеристики неприятных особенностей, дискомфорта и стресса.

Никита Лобачев, Software Engineer, Miro: Мне кажется, токсичность это довольно абстрактный термин и каждый понимает его по-своему. В большинстве случаев то, что называется токсичностью, возникает из-за недопонимания, то есть люди думают по-разному, говорят про разные вещи, из-за этого спорят, и всегда хочется назвать спорящую сторону токсичной.
Токсичность это что-то редкое, откровенно агрессивное неконструктивное поведение, крики, мат, драки, харассмент и всё такое.
Например, в одной из компаний был фронтендовый лид, у меня с ним часто возникали терки по поводу подхода к разработке. Я топил за просто и понятно, а он скорее был за хардкорный инженерный подход. И всегда возникало желание сказать: Чувак, ты же просто не хочешь слушать. Но одно дело, когда ты общаешься один на один, и у тебя возникает такое ощущение, а другое дело, когда с тобой не соглашаются уже несколько людей. Это хороший момент задуматься: может быть, это не ребята токсичные, а вы расходитесь во взглядах, и у вас разные представления о прекрасном.

Станислав Власов, Engineering Manager, Parma TG: Лично я смотрю на это с точки зрения менеджмента. Мое понимание токсичности это неконструктивное поведение относительно ценностей или целей команды или компании, и эта модель может очень легко передаваться другим людям.

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

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

Павел Кирьянов, Technical Lead, Xsolla: Думаю, токсичность это довольно сильный термин, который для меня как для менеджера является ленивым решением.
Токсичность это попытка поставить ярлык на ситуацию, которую можно исследовать и разрешить продуктивно.
Как правило, все неполадки в коммуникациях происходят из-за того, что базовые ценности немножко расходятся. И мне как менеджеру полезно попытаться прокопать вглубь проблемы и понять, чего хотел каждый. Поэтому я не люблю называть токсичностью вообще практически ничего кроме того, что говорил Никита прямое насилие, харассмент, вещи, которые очевидно выходят за рамки общечеловеческих норм поведения.

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

Булат Сальманов, Technical Head of Social Media Marketing Department, SEMrush: Считаю, токсичность это то, что распространяется. Есть поведение агрессивное, есть поведение конфликтное, но оно не распространяется, оно всегда на одном человеке, все на него смотрят как на придурка, и ничего не происходит. Тут не конфликтность, просто человек необычный. А есть негативное поведение, которое распространяется, и меня как менеджера интересует именно оно.

Возьмем пример человек опаздывает на работу, все смотрят, все к этому уже привыкли, всё делают в сроки, а он не успевает. Ну, ничего страшного. А бывает так, что это начинает подбивать других не соблюдать сроки, не укладываться, не приходить вовремя, игнорировать что-то. И это уже токсичное поведение.
Конфликты перерождаются в токсичное поведение, когда начинают влиять на атмосферу в команде.
И такое поведение надо маркировать, замечать, но называть его более точно: Мы стали игнорировать порядок или Мы стали озлоблены друг к другу и не слышим. Давайте что-то с этим делать. Это как пример.

Тактика: как работать с токсичностью


Станислав Власов, Parma TG: Наверное, самый идеальный вариант для менеджера когда команда зрелая и когда она может самостоятельно решить проблемы на мероприятиях типа ретроспективы или каких-то других. Сложнее, если команда проблемы не решает.
Сперва стоит дать обратную связь. Обратная связь очень важна. Особенно если люди сознательные, они могут не замечать токсичного поведения.
Следующий вариант, когда обратная связь не работает нужно продать проблему. Дать понять человеку, что то, как он себя ведет, контрконструктивно его же целям. Если он понимает, что это контрконструктивно его целям, то он понимает, что это его проблема, и он замотивирован эту проблему решать.

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

Булат Сальманов, SEMrush: Как показывает опыт, там, где не соблюдаются интересы сторон, где каждый не высказывается, что ему нужно и что интересно, всегда начинаются проблемы. Но тут есть еще проблема в том, что люди, или не до конца понимают, чего они хотят, или не могут это рассказать.

Первое, что я озвучил что не до конца люди сами понимают свои цели. Приходишь к человеку, спрашиваешь: Что бы ты хотел в карьере, куда хотел бы развиваться? Ну, я не знаю. Мне нормально. И я их понимаю. Я в 32 года только чуть-чуть начинаю понимать, чего я вообще от жизни хочу, так что это сложный вопрос.
В первую очередь всегда нужно узнать интересы сторон. Не заставлять сделать по-своему, а попытаться быть партнерами.
А не могут рассказать это обычно про уровень доверия в команде. Мы тут в команде друг друга можем матом поругать, поделиться чем-то, и нам всем нормально, доверие высокое. А есть команды, где ничего кроме как о работе не говорят.

Павел Кирьянов, Xsolla: Очень важно узнать, какой интерес у каждого конкретного человека, который участвует в конфликте. Но если лид стал токсичным и отстаивает свой личный интерес, втягивая в это всю команду, это довольно значительный фейл. Я люблю термин лидер-слуга. Лид в первую очередь должен интересоваться тем, чего хочет команда, а потом уже своей рыбалкой, наверное.

С другой стороны, если у человека позиция: Я до шести работаю, а после шести катись оно всё, у меня свои дела, то я тоже это понимаю, уважаю. Довольно часто в IT-компаниях есть негласная культура переработок, о которой никто не говорит вслух, но она есть. И люди, которые против этой культуры идут, часто попадают под каток.

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

Никита Лобачев, Miro: Сложно дополнительно добавить к тому, что ребята уже сказали, так как сказали очень хорошо.
Если пытаться идти в обход человека, который тебе не симпатичен, притом, что он отвечает за команду это хороший способ получить конфликт. Наверное, так делать не стоит.

Low social skill и корреляция с токсичностью


Булат Сальманов, SEMrush: Я давно смотрю на себя как на программиста в первую очередь. Все меряют по себе. Я считаю, что одной из больших проблем в IT является инфантильность. Когда мы говорим, например, о таком важном скилле, как понимание ответственности, то начинает доходить, что ни у тебя до конца нет понимания, что такое ответственность по-настоящему, ни у других людей тем более. И подобных скиллов, которые появляются с серьезным взрослением, очень мало у программистов, потому что им приходится мало сталкиваться с серьезным общением в работе.
Ты в основном находишься в логическом мире, где всё императивно, и где ты говоришь: Делай то, делай то, и тебя слушают. В реальной жизни так не работает.
Взросление происходит через стрессы, серьезные проблемы, а такого у тебя не происходит. Ты постоянно сидишь, делаешь работу, тебе платят хорошие деньги, потому что ты молодец, твоя профессия редкая, и ты думаешь: Я действительно красавчик. Когда ты зарабатываешь за месяц больше, чем твои родители вдвоем за год, то начинаешь думать, что ты крутой и везде прав. Думаю, это одна из основных причин вообще появления таких проблем.

Соответственно, стоит работать над осознанностью и над ростом личности, чтобы проходили изменения. Но это не быстрая таблетка с серебряной пулей, а реально очень долгая работа, и она может затянуться. 8 лет работаю над собой и не могу сказать, что что-то серьезно поменялось. Работаю с психологом, и как он мне недавно сказал: Все сейчас носятся с инфантильностью, но это очень простая штука, которая очень просто лечится. Надо сказать человеку: Ты же взрослый. Поступи как взрослый. Я уверен, что ты справишься. Я пробовал. Не сказать, что всегда работает, но вещь хорошая.

Никита Лобачев, Miro: Я могу добавить еще про mindset, откуда эти low social skills могут проявляться. Часто мышление построено в рамках, что есть что-то правильное и что-то неправильное. То есть правильная архитектура, неправильная архитектура, правильный рабочий процесс, неправильный рабочий процесс. И если ты топишь за что-то, что считаешь правильным, то мы за свое не постоим, все средства хороши, и я буду спорить месяц, два, три, сколько потребуется, потому что это правильно. При том, что это достаточно контрпродуктивный подход, и обычно более эффективно думать в рамках продуктивности.
Не важно, что правильно, а что неправильно, и важно, насколько это помогает двигаться вперед.

Станислав Власов, Parma TG: Думаю, это во многом связано, потому что, как мы выяснили, такие проблемы возникают у нас из-за недостаточной коммуникации, из-за того, что в эту проблему вовремя не вмешался человек, который мог бы повлиять на проблему с положительной стороны. Я бы хотел посмотреть на эту тему с другой стороны часто недооценивают необходимость каких-то софтскиллов для разработчиков, а также недооценивают, менеджерскую составляющую, ее ценность для команды, чтобы такие проблемы вовремя решать и они не переходили в токсичность.

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

Никита Лобачев, Miro: Есть хороший rule of thumb для того, чтобы отлавливать у себя по такие токсичные реакции это когда хочется сказать: Да мне всё равно, что эти бизнес/дизайнеры/маркетинг/ребята из другой команды хотят! Мы этого делать не будем, потому что это плохо с точки зрения кода/оптимизации/всего такого.
Если хочется сказать да мне всё равно, то, скорее всего, у вас что-то в коммуникациях идет не так.

Токсичность незнакомых людей в офлайне, онлайне. Как это работает?


Булат Сальманов, SEMrush: Я не люблю говорить фразу Раньше было лучше, но в среде разработчиков на форумах раньше было лучше. В том плане, что ты мог задать вопрос, и тебе на него отвечали, и даже писали за тебя какой-то код, что-то показывали, ты начинал понимать. И это не использовали как выгоду себе, а использовали просто как помощь. А сейчас даже в айтишных форумах уже начинают тебя закидывать всем подряд: Смотри мануалы / читай документацию / PHP отстой / я не знаю, что отстой / всё отстой. И с этой токсичностью сложно бороться увольнением вот основная проблема.

Например, выступаешь ты, а я говорю: А зачем ты такую отстойную презентацию поставил? Тебе не кажется, что тебе даже и показывать нечего?. Если ты пойдешь в оправдание, то ты поддержал мой вопрос.
Если ты скажешь: Вы просто немного неконструктивны. Но если вам интересно, мы можем дальше пообщаться, ты не позволишь моей токсичности дальше развиваться.
Не поощрять такое поведение, не вестись на эмоции. Потому что сложно, когда тебя поливают, удержаться в конструктиве. Мне кажется, это самое действенное, что мы можем делать.

Есть и простейшие решения, например, ранжировать комментарии не по плюсикам, а по карме людей, потому что карму ставят за действительно хорошие ответы, а плюсики ставят, возможно, за смешные шуточки. И это тоже важно.

Никита Лобачев, Miro: Это всё разговор про комьюнити. Есть разные ресурсы, конференции. И здесь примерно так же, как с людьми, друзьями, с кем ты дружишь. Людей на свете много, ты выбираешь, с кем тебе хочется общаться, а с кем не хочется. Начал писать на Hacker News, Habrahabr, StackOverflow, и думаешь: Ага, тут постоянно споры в комментариях, а тут ребята более-менее конструктивно отвечают. Поэтому в следующий раз буду писать в первую очередь сюда.
Следить за реакцией комьюнити, и искать те комьюнити, в которых тебе более конструктивно общаться.
В мире все-таки не три комьюнити. Так же, как с одним человеком поговорил, он не очень. В баре подошел к человеку, он тоже сильно много ругается, про политику говорит, а тебе неинтересно. Наверное, вообще в бары не стоит ходить, в барах не бывает хороших людей.

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

Павел Кирьянов, Xsolla: Я очевидную вещь, наверное, скажу. Я вообще не понимаю, как комментарии в интернете могут быть токсичными. Там же даже нет человека, с которым ты коммуницируешь в реальном времени. Сообщение отделено от автора и ты сам его читаешь, сам себе в голове какой-то образ рисуешь, и этот образ тебя обижает. Если об этом подумать, то звучит как нонсенс ты сам себя вывел на негативные эмоции. Не стоит так относиться к этому, а стоит просто смотреть на все эти споры в интернетах как на продуктивную полезную вещь или непродуктивную и неполезную. В непродуктивной и неполезной просто не участвуем, и всё. Как Никита сказал.

Станислав Власов, Parma TG: Думаю, тут ребята всё сказали. Мне кажется, очень сильно зависит от комьюнити, формата. Если мы говорим про удаленку, то есть про интернет это одно, а если говорим про общение на конференциях, то это другой кейс.

Но в целом, если речь о каком-то поиске ответа на вопрос, то нужно выводить на конструктив. Если речь о том, чтобы просто пообщаться и в этом кабинете такое общение, то думаю, что ценности для присутствия в таком кабинете немного.

Что делать, если люди считают, что токсичный ты сам?


Павел Кирьянов, Xsolla: Мне кажется, отличный способ привлечь модератора в эти коммуникации, чтобы кто-то помог это разрешить, потому что с двух сторон можно бесконечно биться об эту стену непонимания между собой. Здорово поговорить с кем-то третьим. Если в рабочих отношениях это происходит, то это может быть лид, или Scrum-мастер, или любой другой психотерапевт в компании.

Булат Сальманов, SEMrush: Может быть, стоит просто вывести на конструктив. Если говорят про токсичность, то узнать, что не так. Какие-то конкретные кейсы, получить обратную связь в конструктивном виде.

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

Булат Сальманов, SEMrush: Я бы сказал, что спросить напрямую человека: Как, ты считаешь, было бы не токсично?. Он бы тебе свою версию выдал. А тут уже тебе решать, готов ли ты подстраиваться под него и становиться таким, как он сказал, или не готов. Тут тоже надо выбирать. Может быть, ты хочешь оставаться только токсичным и для тебя не так уж и дороги эти коммуникации.

Если ты видишь, что люди довольно откровенно и честно говорят, например, на ретроспективах, о каких-то личных вещах, например, взрослый бородатый мужик говорит: Ты меня расстроил. Меня весь вечер потом колбасило после спора с тобой, это значит, что доверие высокое. И здесь уже можно спрашивать: Токсичный ли я? Есть ли проблемы в общении со мной?.
Мне кажется, в нашей культуре, особенно в России, не очень принято говорить об эмоциях.
Нам в комментариях писали, что принято эмоции подавливать. Поэтому сложно добиться, чтобы кто-то начал говорить: Мне неприятно, как ты говоришь. Мне неприятно, как ты задаешь вопросы. Первым делом нужно начинать строить доверие. Как начинать строить доверие? Вначале надо доверять. Иначе никак. И начинать говорить о каких-то сокровенных вещах для себя. То есть это долгая работа.

Станислав Власов, Parma TG: Я бы еще хотел добавить. Наверное, очень важно иметь в окружении людей, которые могут дать тебе обратную связь. Банально, если есть такие люди, можно просто у них попросить обратную связь, они ее выгрузят. Если что-то есть токсичное, то, если этим людям можно доверять в контексте обратной связи, ты об этом узнаешь обязательно.

Алексей Долгушев, DevRel-бюро: То есть короткая версия совета: Если что-то подозреваешь, спроси.

Павел Кирьянов, Xsolla: Еще офигенно помогает спросить анонимно. Ребята, я не узнаю, кто это написал. Просто напишите, пожалуйста, что вы обо мне думаете.

Никита Лобачев, Miro: У меня таким триггером было, когда количество людей, с которыми у тебя конфликт, больше одного. Если с одним человеком у тебя какие-то сложные отношения, а с остальными нормально, то еще сложно понять. А если видишь, что и с тем, и с тем, и с тем, и с четвертым, и с пятым, то, наверное, либо ты что-то делаешь не так, либо просто это не твоя компания, не твое место, и, соответственно, стоит либо изменить поведение, либо устроиться в другую команду или другую компанию. Если это не персонально, а группа людей, с которыми у тебя сложные отношения, то, скорее всего, что-то идет не так.


* * *
24 сентября состоится третий митап серии. Тема как бизнес и инженеры общаются и договариваются друг с другом. Спикеры CTO и тимлиды из Miro, Яндекс.Практикум, LANIT, Алгоритмика.

Регистрация
Подробнее..

Иван Дёмшин, Head of Engineering в Miro, о продуктовой разработке, смене технологий и эволюции процессов в компании

27.07.2020 14:05:23 | Автор: admin
Это конспект интервью сИваном Дёмшиным, Head ofEngineering вMiro, про историю продукта икомпании, структуру продуктовой разработки, смену технологий нафронте ибэке, эволюцию тестирования, процесс найма иразвития инженеров.



Полную двухчасовую версию можно посмотреть на Ютуб-канале Хекслет.

Оглавление:
История продукта и компании
RealtimeBoard Miro

Как устроена продуктовая разработка
Рабочее окружение
Стек, монолит
Релизы

Выбор технологий и их эволюция
Flash Canvas
Angular React
Сервера и базы данных
Java
Эволюция тестирования
Рост нагрузки и рефакторинг

Процессы в разработке
Техническое решение и code review
Performance Review
Как устроен найм инженеров
Джуниор-позиции

История продукта и компании


Miro платформа для визуальной коллаборации ввиде бесконечной онлайн-доски (online collaborative whiteboard platform). Ключевое слово коллаборация, совместная работа, соответственно, ключевые метрики, покоторым мымеряем свою эффективность, количество коллаборативных досок иколлаборативных сессий, которые случаются впродукте.

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

Наша целевая аудитория продуктовые команды, которые работают изодного или разных офисов. Чаще всего Miro используют для проведение воркшопов, стратегических сессий, мозговых штурмов, agile-практик (планинги, ретроспективы).



Яруковожу разработкой вMiro. Явырос вПерми ипродолжаю здесь жить. Компания исторически появилась вПерми, отсюда родом наши основатели. Большая часть нашего отдела разработки находится сейчас здесь, ав2019 году мызапустили иактивно развиваем второй инженерный офис вАмстердаме.

Раньше яработал взаказной разработке: начинал спостроения аналитических хранилищ данных вкачестве разработчика, потом проектировалих, азатем руководил большими проектами. В2016 году присоединился кMiro, когда вкомпании работало 30человек. Стех пор мысильно выросли: сейчас унас пять офисов, 400человек, изних 140инженеров.

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

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

Примерно до2016 года все сотрудники работали вПерми. Пользователи сами платили заподписку, нокнам уже приходили компании ссотнями итысячами сотрудников, которые хотели заключить договора иполучать инвойсы. Для работы стакими компаниями нам нужны были продажники, поэтому мынаняли Head ofSales для создания sales-команды вАмерике.

Затем появился первый сотрудник Customer Success команды, вАмстердаме, и также начал строить свою команду.

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

Смена названия


Оребрендинг мызадумывались ещё в2015году, носделали его витоге в2018. Наше прежнее название RealtimeBoard длинное исложное. Внём часто допускали ошибки, сокращали доRTB или, что самое плохое, вообще его забывали. Кроме того, оно неэмоциональное, заним нет истории. Мыхотели сделать новое название коротким, ёмким, говорящим, запоминающимся.

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



Кновому названию быстро привыкли имы, ипользователи. Мыбыстро растём, поэтому несколько миллионов новых пользователей даже незнают отом, что мыназывались иначе. Приятным бонусом ребрендинга стали награды от European Design Awards за айдентику, которую мы разработали в рамках ребрендинга совместно с европейским агентством Vruchtvlees.

Как устроена продуктовая разработка в Miro


Вся команда продуктовой разработки из170 человек находится вПерми иАмстердаме: инженеры, продакты, продуктовые дизайнеры, скрам-мастера.

Мне раньше было сложно представить, что так много человек может работать над одним продуктом. Носегодня язнаю, что вUber, Slack иAtlassian над одним продуктом работают тысячи инженеров. Мыпродолжаем расти ипонимаем, что нас сейчас явно недостаточно, иследующая целевая точка вмоей голове 300 человек вразработке, апотом мыпродолжим расти дальше. Это непросто число изголовы. Унас есть стратегическое планирование, мыпонимаем, где мыхотим быть через два года, через пять лет ичто нам для этого нужно сделать.

Сточки зрения оргструктуры есть гильдии: фронтенд, бэкенд, QAитак далее. Для работы над проектами они объединяются вкросс-функциональные команды.

Команды распределяются по ключевым направлениям:
  • Горизонтальный продукт основная функциональность продукта, которую видят все пользователи: стикеры, текст, шейпы, фреймы и т.д.
  • Системное направление отвечает за core-платформу и инфраструктуру.
  • Growth всё про рост числа пользователей: активация, вовлечение, возврат, монетизация.
  • Enterprise доработка продукта для крупных компаний, у которых много специфических требований. Во-первых, тысячи и десятки тысяч их сотрудников пользуются Miro, а это значит что для их аккаунтов нужны разные права доступа и удобные инструменты управления ими. Во-вторых, есть международные стандарты качества и безопасности для SaaS-продуктов, которым мы должны соответствовать. Мы не делаем кастомизацию под конкретного пользователя, а выбираем, что необходимо сделать в соответствии со стандартами и требованиями большинства крупных пользователей.
  • Платформа мы запустили открытую бету в 2019 году. Уже есть открытый API, кабинет разработчика, marketplace, всё это нужно поддерживать и развивать, давать больше возможностей внешним разработчикам, которые хотят создавать для себя и других дополнительную ценность на базе нашего продукта.
  • Основные юзкейсы продукта новое направление, которое мы активно масштабируем. Пользователи приходят в продукт, чтобы решить конкретную задачу, но большинство способов, с помощью которых они её решают в Miro, можно объединить в несколько групп: Meetings & Workshops, Ideation & Brainstorming, Research & Design, Agile Workflows, Strategy & Planning, Mapping & Diagramming.

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


Рабочее окружение


Основной софт инженеров: IntelliJ Idea, Jira, Slack, Zoom, Miro, Confluence. Большинство сотрудников работают на MacBook, большинство инженеров на MacBook Pro, некоторым покупаем более мощные машины при необходимости.

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

Стек, монолит


Фронт у нас на Typescript, React и AngularJS. Бэкенд Java. Базы данных Redis, Postgres, для кластерного взаимодействия Hazelcast и ActiveMQ. Хостимся в Amazon, в одном дата-центре. В продакшене порядка 400 серверов. Application servers, которые обрабатывают доски пользователей, бывает до 100, всё автоматически оркестрируется.

Используем стек от Atlassian: Jira, Bitbucket, Bamboo и собственные скрипты, которые прикручены к Bamboo и позволяют всё раскатывать на сервера. Пока все релизы один большой релиз на фронт и один большой на бэк. Сейчас думаем, как сделать, чтобы этих релизов было больше.

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

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

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

Релизы


Сама сборка занимает 15-20 минут, включая выполнение юнит-тестов. End-to-end тесты могут выполняться до 40 минут. Весь процесс занимает полтора-два часа, чтобы довести мастер до релиза. Это долго, нам ещё есть над чем здесь поработать.

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

Выбор технологий и их эволюция


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

Маленькие компании, которые только начинают иищут product market fit, бегут быстро, быстро собирают MVP ибыстро ихвыбрасывают. Для них важнее найти рынок, анесоздать сложные технические решения. Нокогда рынок найден технические решения выходят напервый план, потому что позволяют создать запас прочности для роста ибезопасности.

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

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

Больших смен технологий у нас было несколько, на фронте и на бэкенде. Приведу несколько примеров.

Flash Canvas


До 2015 года весь фронт был на Flash, потом Flash начал умирать и мы перешли на HTML и Canvas. Смена стека хорошо сказалась на производительности и удобстве продукта, привела к заметному росту аудитории. Переход занял примерно год, это был большой и сложный проект. Статья про детали этого проекта.

Сейчас мы рассматриваем переход на WebGL, но пока нет чётких доказательств, что оно того стоит.

Angular React


Последние пару лет мы постепенно переходим с Angular JS на React. Основные причины:
  • React позволяет лучше типизировать, а в дальнейшем это позволяет лучше рефакторить код и гарантирует, что ничего не отвалится.
  • React даёт больше возможностей для оптимизации приложений. Когда мы внедряли ленивую загрузку и разбивали тяжёлый монолит на фронте, Angular вставлял палки в колёса. Нам пришлось найти обходные пути для оптимизации загрузки с помощью Angular, а всё новое уже пишем на React, его же используем при глоабльном рефакторинге.
  • React хорош с точки зрения найма, так как найти разработчиков на Angular JS сильно сложнее.


Сервера и базы данных


В 2015 году мы перехали с арендуемых серверов Hetzner на хостинг в Amazon. Больше года идёт проект по переносу основной базы данных с Redis на PostgreSQL. У нас есть статьи об этом: проектное управление миграцией данных, создание отказоустойчивого кластера.

image

Наш кейс осложняется тем, что с Key Value хранилища мы переезжаем на SQL базу. Рефакторинга много. Важно делать всё так, чтобы приложение не останавливалось. Это как поменять колесо у едущей машины, потому что база данных фундамент, на котором стоит приложение. Непосредственно для контента досок мы реально делали всё без maintenance. Да, процесс перехода затянулся по времени, зато пользователи ничего не заметили, продукт работал.

Стабильность продукта ключевой фокус. Пользователи хранят много контента в Miro. Соответственно, если пользователь запланировал сессию или встречу, подготовил для неё доску с контентом, а продукт в этот момент недоступен это провал, контент использовать невозможно. Если условный Zoom можно быстро заменить на Hangouts, то контент быстро заменить нельзя. Поэтому одна из наших ключевых задач чтобы контент для пользователей был всегда доступен.

Java


Java очень сильно помогает нам в плане производительности и ресурсов разработчиков, которые мы можем найти. Знаю, что Booking переходит с Pearl на Java, потому что они устали переучивать своих инженеров.

К нам приходят инженеры с С++ и DotNet и нормально адаптируются. Если ты опытный разработчик, пробовал разные технологии и знаешь, как строится система, то погрузиться в новый язык уже не так сложно. Главное, чтобы инженер придумывал правильные решения, а в язык закопаться он точно сможет, я верю в это.

Эволюция тестирования


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

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

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

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

image

Подробное описание всех этапов нашего QA-процесса.

Рост нагрузки и рефакторинг


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

Запас прочности понадобился не только на бэкенде, но и на фронтэнде и клиенте, так как в продукте возросло количество синхронных работ. Если раньше на одной доске могло работать одновременно 20 человек, то сейчас это 300 человек. Наши фронтэнд-инженеры много занималась и продолжают заниматься производительностью загрузки. Например, мы делаем так, чтобы dashboard со списком досок грузился отдельно от всего остального и делал это быстрее, чем раньше. А если пользователь переходит прямо на доску, не через dashboard, то должен грузиться код и контент доски, без всего остального.

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

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

Процессы в разработке


Техническое решение и code review


Любая задача начинается с подготовки продуктового решения. Продуктовое решение это ответ на вопрос Что мы будем делать?. Продакт-менеджер на основе продуктовой стратегии и OKRs проводит большое исследование, чтобы выяснить, чего сейчас не хватает пользователям в нашем продукте. На основе исследования продакт описывает решение. Продуктовая гильдия обсуждает решение, дорабатывает при необходимости.

На базе продуктового решения формируется техническое решение, которое отвечает на вопрос Как мы будем это делать?. Его разрабатывают инженеры команды, которая будет реализовывать функционал. Техническое решение проходит несколько процессов review:
  • с командами, с которыми есть пересечения в функциональности;
  • security review по компонентам, которые мы будем затрагивать в архитектуре;
  • как мы будем деплоить результат.

После начинается непосредственно разработка. Важно, чтобы code review не тормозил разработку, поэтому недавно вместо обязательного получения двух code review мы внедрили персональную ответственность на уровне компонентов. Теперь на уровне кода мы всегда знаем, кто отвечает за этот кусок, что сильно облегчает коммуникацию при разработке. Соответственно, как только ты внёс изменения в код, автоматически назначается reviewer, владелец этого кода. Если код твой, то review делает участник твоей команды.

Зачем мы ввели персональную ответственность? Раньше было несколько человек, старичков, которые знали, как работает весь продукт и могли проверить любой кусок кода. Но по мере роста продукта возможностей этих людей стало не хватать, они уже не могли знать про всё, что происходит в разработке. Процесс code review начал тормозить остальные процесса, было непонятно, к кому идти на code review. Тогда мы начали нести всю необходимую компетенцию по конкретным блокам продукта в команду, которая работает над ними. Так команды смогли проводить code review самостоятельно. В своё время это помогло нам сильно ускориться.

Performance Review


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

На основе этой картины тимлид с каждым членом команды формирует персональные планы развития: сотрудник сам говорит, куда хочет развиваться, а Performance Review подсвечивает его сильные стороны и и пробелы.

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



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

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

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

Как устроен найм инженеров


У нас цепочка найма стандартная для России и для Европы. В России воронка найма уже, поэтому первое интервью может проводить не рекрутёр, а сразу hiring manager (обычно это тимлид команды, в которую мы нанимаем человека) после того, как рекрутер обработал резюме и отсеял неподходящие под требования вакансии.

У меня ощущение, что в России гораздо меньше инженеров активно ищут работу, по сравнению с Европой, потому что не хотят рисковать. А когда многие компании вошли в зону риска из-за изоляции, люди стали ещё менее склонными к риску и смене работы.

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

После скрининга тестовое задание, затем техническое интервью, которое включает в себя в том числе обсуждение тестового задания. Затем встреча с командой, в которой будет работать кандидат. Для нас это обязательный этап, потому что он помогает в первую очередь понять culture fit кандидата, а не его технические навыки.

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

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

Джуниор-позиции


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

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

Высокие требования при найме


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

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

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

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

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru