Табота Олег@nProfessor
Руководитель команды общих компонент в Яндекс.Еде
Информация
- В рейтинге
- 852-й
- Откуда
- Москва, Москва и Московская обл., Россия
- Дата рождения
- Зарегистрирован
- Активность
Специализация
Бэкенд разработчик, Фулстек разработчик
Ведущий
От 10 000 $
Symfony
PHP
Высоконагруженные системы
ООП
Базы данных
SQL
У нас так же, и это не очень помогает решать описанную проблему)
У нас для каждого сервиса генерируется "клиент"(условно SDK для моего сервиса). И все кто хочет делать запросы в мой сервис должны использовать именно его. А настройками этого "клиента" управляю я, как владелец сервиса. То есть я сам говорю в какой эндпоинт у меня какие тайминги и какая политика ретраев.
Проблема во всей цепочке, например A -> B -> C
У вас может быть идеально настроено взаимодействие A -> B и B -> C по отдельности, с точки зрения владельца сервиса B и отдельно владельца сервиса C. А вся цепочка будет не правильной.
Нужен инструмент который будет показывать и сингализировать о неправильных настройках во всей цепочке
Документация есть. Каждый раз когда делается какое-то значимое изменение, пишется RFC, которое должны окнуть, либо все ответственные затрагиваемых сервисов, либо гильдия Архревью, в зависимости от масштаба изменений.
Но вот теперь представьте, вы делаете такой масштабный проект, у вас написана сотня RFC. Напомню, проект делался почти год. Конкретно стрельнувший функционал был сделан месяца за два до описанных в статье учений.
И вы подходите к моменту когда у вас все вроде работает, и нужно убедиться что какой то компонент стал не важным.
У вас только один способ, прочитать все документации, и надеяться что вы все поймете, все запомните и что данные в документации еще актуальные.
Так что наличие документации не может дать вам представление о том как работает вся систем, а только помогает разобраться с конкретными вопросами.
Конечно только если вы не про документацию(визуализацию) которая описывает все ваши процессы например в формате BPMN. Если у вас Camunda или аналогичная система, где все наглядно и понятно.
У нас пока нет визуализированного механизма просмотра работы всей системы, но скоро появится. Обязательно про него расскажу в следующий раз.
Здесь речь про некоторое когнитивное искажение. Естественно если посмотреть код, то легко понять как конкретно в данном месте что происходит. Но когда все постоянно говорят "Синхронизация между A и B" ты просто "веришь" что оно идет на прямую, и даже мысли не возникает проверить.
Еще очень важно понимать, что данный проект в совокупности делали около 50+ человек, и вот конкретно этот момент был одним из сотни других, и изначально не выделялся своей важностью, и как тогда казалось, не требовал пристального внимания.
Человеческий фактор, к сожалению никто не отменял )
Вот как выясняется, просто так )
Мы совсем недавно встроили такую же проверку как у Васи, и получили список сервисов где есть подобная проблема. Создали всем командам отвечающим за эти сервисы задачки с просьбой либо исправить, либо описать почему исправляться не будет(например альтернатива сложнее и не рентабельно)
В итоге 17 сервисов исправили, 3 описали причины по которым не хотят/не могут исправить.
Оказалось что в большинстве случаев это просто ошибка, не посмотрели, не подумали, не посчитали важным. И решилось просто выносом вызова из транзакции не влияя при этом на бизнес логику, атомарность и так далее.
да, логи у нас пишутся именно так.
Я не совсем правильно понял ваш вопрос, я под "логирование" понял отправку в Sentry, так как из-за этого случился инцидент
Это специфика PHP.
У нас используются короткоживущие процессы php-fpm. Если я не ошибаюсь то с использованием fastcgi_finish_request, то есть мы отдаем ответ клиенту, и дальше продолжаем работу, записывая логи, отправляя метрики и так далее. То есть на клиент это не влияет, он получил ответ и пошел дальше, но процесс PHP-FPM продолжает работать.
Возможно сейчас уже проще поддерживать ассинхрон в PHP (к сожалению не знаю), но 10 лет назад, когда писался этот код, с ассинхроном все было плохо
Возможно действительно многие проекты так и не доходят до стадии полного отказа от монолита. Что бы этого не произошло у нас, мы принимаем ряд мер.
1) Устраиваем активности типа "Новогодняя чистка", когда выделяется неделя-две для всех желающий разработчиков поудалять код в монолите. Естественно с призами. Вот в этом декабре такая прошла, в рамках которой мы удалили больше 300к строк кода из монолита.
2) Мы никогда не шарим базу монолита. Мы убеждены что шарить базу - это супер рискованное решение, которое приводит к трудно диагностируемым проблемам.
3) Как критерии для выноса, мы используем количество вносимых в какой-то участок кода или бизнес логики изменений, сложность выноса этой логики, нагрузка которая приходится на этот участок, количество инцидентов связанных с этим кодом.
Скорее всего монолит у нас останется в каком-то виде на долго, но там точно не будет критичной функциональности. Скорее всего это будет черная коробочка которая по сути ни на что не влияет. Просто потому что избавление от нее по соотношению цена-качество не в пользу выпила.
А так у нас уже больше 180 атомарных микро сервисов
По этому поводу очень хорошо рассказал CTO Яндекс.Еды https://youtu.be/F6NN8pW2Wac?si=UdUsWAlRycUHPabw
Да, действительно у нас есть кейсы где так происходит(Я вроде в видео версии рассказывал, а тут не упомянул).
Например мы выносили сущность "Бренд ресторана", и эта сущность пронизывает огромное количество частей монолита.
Мы создали сервис, CRUD операции перевели на него, и данные оттуда "реплицируем" в старую табличку. При этом мы весь основной функционал в монолите переключили на получение данных из этого сервиса, оставив старую табличку только для самых не критичных вещей. Потому что стоимость переключения всего равнялась примерно 6 человекомесяцам работы, а профита было бы не очень много.
Все новые сервисы брали данные из сервиса брендов, и если нужно было расширить функциональность, мы принципиально не расширяли старую табличку. Она помечена как деприкейтед. Если в монолите кому то требуется новая функциональность сервиса брендов, он переходит на сервис как на первоисточник.
У нас таких кейсов не много, и если они создают проблемы мы в первую очередь стараемся от такого уйти. Это состояние считается не законченной задачей.
У нас вся схема АПИ хранится в YAML файлах, и по ним генерируются клиенты. Есть чекеры которые запрещают тебе ломать обратную совместимость. Так что любые изменения у нас прямо и обратно совместимы.
Но вопрос с тестами для нас все равно актуальный. Иногда приходится переписывать сотни тестов что бы уйти от БД в пользу использования мока ответа сервиса. Иногда тесты просто удаляем, потому что они начинают проверять просто мок.
Здесь у нас 2 момента:
1) Мы просто начали писать больше интеграционных тестов
2) Делаем новые инструменты для более удобного написания и запуска этих интеграционных тестов. То есть, можно написать тест так, что бы при релизе одного сервиса прогонялись тесты всех связанных сервисов с реальными походами между ними.
Это не какой-то законченный проект, это больше направление движения. У нас все команды стараются делать новую функциональность в новых сервисах, и для этого им приходится вытаскивать часть функциональности из монолита, потому что мы специально создали такие условия, что бы в монолит большая сложная функциональность больше не добавлялась.
Так постепенно мы его и распиливаем.
Если отвечать на вопрос, то можно ответить так, в большинстве случаев хватает 2-3х разработчиков что бы за 1-2 месяца вынести значимую часть кода в сервис. Но иногда это бывают и дни, иногда месяца, все зависит от многих факторов.
Мы стараемся в планирование новых фич закладывать сроки на реализации их в новом сервисе. Если бизнес ни в какую, нужно срочно и все, думаем над компромиссами, например в рамках задачи сделать функцию в монолите, но часть функциональность все таки написать в сервисе, что бы в следующий раз менять уже там.
И очень помогает подход, который я описал в статье в разделе "Вынос эндпоинта получения оффера". То есть мы просто поднимаем сервис прокси, и там добавляй функциональность которая нужна. Да, не всегда это подходит, но в большенстве случаев работает.
Спасибо за хорошие вопросы!
Ответ на первый. Мы выносим сущностями. Это значит, что, скорее всего, уедет или вся табличка, или ее часть, или даже несколько. Чтобы посчитать нагрузку, мы начали собирать метрики запросов. В Симфони есть листнеры, и мы подписались на события запроса в базу данных. Там ловим SQL-запрос, парсим его и шлем в метрики в особом формате. После чего в метриках мы можем увидеть, сколько было запросов, к какой табличке, через какие эндпоинты, скрипты или консьюмеры. Если, например, при инсерте будет добавлено 100 строк, то мы увидим только одно обращение, но это дает нам примерное понимание профиля нагрузки. Для более тонкого понимания можно посмотреть метрики MYSQL или просто посчитать количество добавленных данных.
То есть мы смотрим на RPS ручки, которую выносим, и/или на RPS табличек, которые выносим. Смотрим на сами таблички: для чего они, что делают, какой у них профиль нагрузки. Может быть, это табличка с логами, где только INSERT, или табличка со статической информацией, где только SELECT.
По этим данным чаще всего становится понятно, что нужно делать, но момент докручивания никто не отменял. Действительно, при запуске какой-то фичи нам может понадобиться, например, увеличить флейвор базы сервиса. Да, такое бывало, но в основном мы попадаем.
Ответ на второй. На него сложнее ответить, потому что универсального ответа нет. Каждый случай индивидуальный. Мы пытаемся мыслить адекватно, прогнозируя, к чему приведет то или иное изменение. Где-то продумываем фолбеки (деградацию), где-то переосмысливаем сущность и формат ее хранения и переписываем на другое поведение, где-то получается заюзать кеш, как, например, с сервисом стран.
Потому что плагин создан для проекта написанного на PHP7 где не было таких возможностей.
Если сразу проставлять типы, наверно проблем описанных в статье у вас не будет. Но если у вас легаси проект с сотнями подобных кусков кода, то плагин вам поможет
Ни в коем случае никого не критикую. Если проект жив столько лет, значит на разных этапах разработки все делали какие-то правильные вещи, что позволило выжить среди конкуренции. Это касается как разработчиков так и менеджмента.
Подскажите, какой конкретно текст вызвал такое ощущение? Возможно стоит его подкорректировать.
Могу только предположить что вас смутила эта строчка
или эта
Но это не про разработчиков, это про объективную реальность
Легаси - означает что код старый и уже нет овнеров кода. Любой код устаревает и становится легаси, и разработчики тут не причем.
Разработчиками разного уровня - это включает как разработчиков стартапа, так и разработчиков уже внутри яндекс еды, и это тоже просто факт без какого-то негативного оттенка.
PHPunit + Xdebug - подсчет покрытия кода тестами. В отчете есть данные по количеству строк кода, классов, методов и функций.
При том подсчете мы исключили все что смогли.
Ради интереса поставил плагин. Но возникает несколько вопросов, какой код вы анализировали?
В приведенном в статье примере, мы анализировали только файлы с бизнес логикой, исключая файлы тестов и Dto файлы, всякие клиенты к сервисам и другие не важные штуки.
Вот если ничего не исключать
Если исключить все Юнит тесты
Dto и другие вещи которые не нужно отслеживать, я уже исключать не буду.
А что исключили вы? Интересно что бы сравнить.
Как я писал в статье, подсчет покрытия тестами мы провели уже постфактум. Признаю, было ошибкой не сделать этого перед началом работы. Из-за этого мы потратили около двух недель на отлов остальных ошибок в тестинге. А это, в свою очередь, означало еще 2 недели фриза на релизы и 2 недели дополнительной работы команде тестировщиков.
Исходя из полученного опыта могу сказать, что поэтапный переход был бы дольше, но каждый этап — намного проще.
Возможно, мы бы после первого этапа сделали какие-то выводы и скорректоровали свои планы для следующих этапов.
Основную проблему создали не изменения в фреймворке или PHP, а изменения в сторонних библиотеках, которые не поддерживались или полностью меняли интерфейс. Не исключено, что у некоторых из них интерфейс менялся несколько раз, поэтому пришлось бы делать одну и ту же работу по кругу.
В следующий раз, скорее всего, пойду по поэтапному плану ?
Несомненно, если бы с самого начала было больше рук, решили все быстрее.
Цена на поддержку тестов кажется оправданной для такого большого проекта.
Так посчитала автоматика при подсчете покрытия тестами. Это без учета наших бандлов, только монолит.
Потенциальные. Конечно, вероятность того, что появится какая-то уязвимость, которую можно было бы эксплуатировать у нас, минимальна. Но все равно это потенциальные риски, которые нужно закрыть