Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Недавно нам довелось перевести на актуальные рельсы устаревший сервис. На этой махине у заказчика завязано много процессов — от таргетированной рекламы фармпрепаратов до доставки пробных образцов на реальный адрес. Но она не обновлялась 8 лет, и работала на древнем фреймворке Yii 1, который не поддерживается с 2015 года. Даже незначительные изменения нужно было вносить 3 недели.
Меня зовут Никита Швыряев, я руководитель отдела разработки компании «СмартАп Технолоджи». Этот проект мы перепиливали 4 месяца. Расскажу подробно, как это было, и что получилось.
Любые изменения вгоняли сервис в ступор, мы искали причины
Клиент обратился к нам, чтобы исправить систему. Вот как она была устроена:
Система показывает докторам рекламу новых фармпрепаратов, а также создает отчеты по рекламным кампаниям, доступности и активности врачей.
У системы есть две основные составляющие:
С внутренним сервисом — системой управления данными врачей — работают администраторы. Они поддерживают актуальность контактов врачей, управляют их доступами к сайтам и публикациями. Через эту часть сервиса можно проверить доступность врачей, настроить и запустить маркетинговую кампанию.
Вторая часть системы — публичный сервис — это сайты, похожие на социальные сети, где врачи обмениваются опытом. С точки зрения системы активность врача на тематическом сайте позволяет показывать ему узко таргетированную рекламу новых медицинских разработок.
Проблема системы состояла в том, что даже незначительные изменения нужно было вносить 3 недели. Причиной этого чаще всего является повышение связанности компонент системы. Также на скорость разработки может влиять неудачный дизайн кода. Еще один возможный фактор — неверный выбор инструментов, когда вместо того, чтобы использовать сильные стороны фреймворка разработчики вынуждены снова и снова бороться с его слабостями.
Определив круг возможных проблем, мы перешли к проверке гипотез.
По истории изменений в коде заметили, что даже небольшое расширение функционала требовало изменений в большом количестве файлов проекта. Это подкрепляло предположения о высокой связанности кода.
При обследовании бросилось в глаза обилие кода, связанного с двусторонней синхронизацией данных двух баз и проверкой актуальности и корректности каждой из них. При этом данные хранятся одни и те же, две базы созданы не ради агрегации или модификации. Такое хранение неоправданно усложняет систему.
Проблемы с производительностью требуют внимательной диагностики процессов чтения, обработки и записи данных. Часто в них можно найти много возможностей для оптимизации. Нужно детально обследовать хранилища и процессы, связанные с дублированием данных, если такое имеется. В случае, когда одни данные, возможно, в разном виде, лежат в нескольких хранилищах, может возникать несоответствие и рассогласованность в данных, цикличные обновления.
В системе заказчика данные были сохранены в ненормализованном виде, все лежало в одной большой таблице. К примеру, если у доктора было два адреса электронной почты, то для этого доктора было две полных дублирующих записи (ФИО, больница, должность и прочее) с разными адресами электронной почты. Мы разделили эту таблицу на десять таблиц со связями для нормализации и устранения дублирующих записей.
С сервисом синхронизации данных тоже было не всё гладко. Он занимается копированием информации по графику из одной системы в другую. Эта связь — двунаправленная. При таком подходе пользователи разных систем могут видеть актуальные и устаревшие данные в один момент времени. При одновременном изменении одних данных в разных системах могут возникать конфликты.
Запрос доступности списка докторов для доставки сообщения по определенным каналам выполнялся от 7 до 30 минут. Мы проанализировали SQL, оптимизировали его и сократили время выполнения. Теперь этот запрос обрабатывается от 30 секунд до 3 минут.
Система управления данными врачей создана давно. В ее основе лежал фреймворк Yii 1, поддержка которого прекратилась в 2015 году. Исправления безопасности теперь можно найти не на все уязвимости, что-то приходится закрывать силами команды. К тому же, интерес к устаревшему фреймворку в среде разработчиков угас. Из-за этого на многие вопросы приходится отвечать самостоятельно, без возможности найти решения подобных проблем в интернете.
Кроме того, при проектировании архитектуры была допущена ошибка. Два компонента взаимодействовали друг с другом не только посредством API, но и через запись в базу MySQL. Если мы хотим добавить новую бизнес-логику или скорректировать существующую, придется делать это сразу в двух системах. Велики риски нарушения согласованности данных.
Изменили структуру хранения данных
Из двух информационных контуров системы: хранилища данных докторов с панелью администратора и публичных сайтов, наибольшую ценность для заказчика представляют данные. Именно их «покупают» клиенты для своих рекламных кампаний. В старой версии системы информация о медицинских работниках копировалась из основного хранилища в базу данных сайта. При этом сам доктор мог корректировать свою информацию, например, адрес электронной почты. Эти изменения сохранялись в базе данных сайта и синхронизировались в системе хранения данных докторов.
При таком подходе актуальность данных поддерживается в двух идентичных хранилищах, и часть бизнес-логики, связанной с подготовкой и проверкой данных, скопирована в обе системы. Если в такой логике возникнут различия, в одной из систем появятся «неверные» данные. Это провоцирует дополнительные затраты ресурсов на разработку и тестирование изменений.
Взаимодействие компонент системы было улучшено так:
В обеих системах созданы API для всех сценариев взаимодействия.
Отключен прямой доступ системы управления контактами к базе данных публичных сайтов.
Удален сервис синхронизации. Все нужные API вызываются в момент обновления данных.
В новой архитектуре вся ответственность за данные докторов лежит на основном хранилище. Если сайтам нужно получить или изменить данные врача, они обращаются в систему хранения через API. Данные и логика их обработки теперь размещены в одной системе без дублирования.
В нашем случае это было возможно, потому что система работает под невысокой нагрузкой и не выполняет тяжелых операций при обновлении данных. В ином случае потребуются дополнительные сервисы вроде очереди.
Сократили расходы и ушли от устаревшего фреймворка
Неравномерная нагрузка на систему в разных задачах привела к решению использовать Lambda-функции в облаке Amazon Web Services (AWS Lambda) вместо выделенной виртуальной машины.
Компания и раньше пользовалось облачными услугами AWS, но все серверы заказчика запускались на виртуальных машинах в облаке. Это невыгодно по финансовым тратам, если сервисы с высокой разовой нагрузкой редко используются. Дело в том, что AWS выделяет заказчику вычислительные мощности, которых должно хватать на пиковые нагрузки. Если такие нагрузки случаются редко, компании приходится оплачивать фактически неиспользуемый запас ресурсов. Так было и в данном случае, добавление новых докторов зачастую происходит группами по больницам один раз в несколько дней. Статистика по докторам генерируется по графику один раз в день, выборка доступных для контакта докторов из списка — тоже разовая операция, которая осуществляется в рабочие часы.
В этом случае выгодно использовать функции AWS Lambda. Под них хостинг не выделяет мощностей и не держит их постоянно запущенными в работу.
Когда вызывается соответствующая функция, хостинг оперативно выделяет под нее вычислительную мощность на машине нужного типа, выполняет функцию и вскоре отзывает ресурсы обратно, если не происходит повторных вызовов.
Заказчик при этом платит только за фактическое время использования ресурсов. Такие функции легковесны и требуют минимального окружения для запуска.
Российские облака от Yandex и Mail.ru тоже поддерживают Lambda-функции. Это значит, что «СмартАп Технолоджи» может реализовывать подобные проекты для российских клиентов на базе российских облаков с использованием доступных сервисов.
Интерфейс администратора реализован в виде статичного сайта Single Page Application (SPA). SPA — это одностраничное веб-приложение, которое загружается на одну HTML-страницу и имеет множество преимуществ: скорость, хороший пользовательский интерфейс (UX) и полный контроль HTML-разметки. Он также дает администраторам возможность взаимодействовать с Lambda-функциями.
Такой статичный сайт можно разместить где угодно, даже не поднимая веб-сервер. Например, можно использовать хранилище файлов или CDN, в зависимости от требований к скорости открытия. Мы выбрали простое хранилище, так как у заказчика не было жестких требований к скорости обмена данными.
SPA-сайт администратора реализован на базе фреймворка Vue 3. Если сравнивать Vue с другими популярными фреймворками, то у него нет такого широкого набора готовых компонент, как у React, и богатых возможностей структурирования кода, которые дает из коробки ПО Angular. Но поскольку интерфейс администратора системы не отличается высокой сложностью, наиболее полезен инструмент с быстрой кривой обучения.
Разработчику не нужно думать об обновлении данных, которые отображаются на экране. Поэтому можно обойтись минимальным набором возможностей, зато сделать проект быстро и легко развиваемым. Тогда в будущем заказчик сможет при необходимости безболезненно сменить поставщика ИТ-услуг.
Чтобы минимизировать инструментальные средства для создания Lambda-функций, решено использовать JavaScript.
Таким образом, интерфейс администратора мы можем отдавать пользователю из статичного хранилища типа S3 или с помощью CDN. Запросы к API направим на специальный сервис API Gateway, который будет подбирать подходящий обработчик. Сами обработчики будут реализованы в AWS Lambda.
Итоги, и что ждет сервис в будущем
Удалось реализовать инкапсуляцию данных и логики их обработки в одной системе. Такой подход позволил удалить логику обработки данных из второй системы. Весь код, трудившийся над синхронизацией и обеспечением согласованности данных, стал не нужен. Система была упрощена, это напрямую сказалось на скорости разработки нового функционала.
На треть снизилось количество регрессионных багов при разработке новых функций. Такой результат — следствие снижения связанности компонент системы и устранения дублирования в данных и бизнес-логике.
Теперь код расположен в одном месте, а отдельные функции имеют четкие границы и не оказывают влияния на работу других функций.
Ускорение процессов разработки измерялось на новой функциональности системы в течении трех месяцев после завершения проекта модернизации. Разработка ускорилась, как минимум, в два раза: параметр Time To Market для новых запросов сократился в среднем с двух недель до одной.
Внедрение Lambda-функциональности в облаке вместо выделенной виртуальной машины означает, что заказчик оплачивает пользование облачными ресурсами только по факту реального выполнения своих задач. Таким образом, трансформация системы позволила заказчику не только оперативнее реагировать на изменение требований рынка и быстро воплощать новые идеи в жизнь, но и экономить на стоимости владения. Цена и скорость внесения изменений стали достаточно низкими для того, чтобы легко и быстро удовлетворять возникающие потребности внутренних заказчиков.
А у кого из вас был опыт перепиливания устаревших сервисов? Давайте обсудим его в комментариях. Также охотно ответим на вопросы по нашему кейсу.