Jira Cloud — одна из самых популярных платформ для управления проектами и отслеживания задач, используемая миллионами команд по всему миру. Компания Atlassian, разработчик Jira, постоянно улучшает производительность и масштабируемость сервиса, чтобы подстраиваться под растущие требования клиентов. Одним из важнейших технологических достижений в этом направлении стал переход на использование Protobuf (Protocol Buffers) в качестве формата сериализации данных в Jira Cloud. Этот шаг позволил повысить скорость работы, снизить нагрузку на инфраструктуру и создать платформу, способную обрабатывать огромные объемы данных с максимальной эффективностью. В основе Jira лежит большой монолитный код с 18-летней историей развития, который постепенно утратил оптимальность для облачной среды.
Для решения этой проблемы Atlassian взялась за переработку архитектуры, выделяя ключевые компоненты в микросервисы с определенными зонами ответственности. Основное внимание уделяется так называемому «Issue Service» — сервису, отвечающему за хранение, получение и обновление данных о задачах (Issues) в Jira. Он построен на хранении данных в формате ключ-значение с документной моделью и рассчитан на поддержку до миллиарда задач в одной инстанции Jira. В процессе эволюции «Issue Service» проходит три этапа внедрения, начиная с кэширования данных для быстрой выборки, затем его использования параллельно с монолитом и конечного перехода к полной эксплуатации сервиса для чтения и записи данных. До перехода на Protobuf данные передавались между сервисами и в кэше Memcached в формате JSON.
Несмотря на широкое распространение JSON, он имеет ограничения по производительности и объему передаваемых данных. Atlassian провела внутренние тестирования нескольких альтернативных форматов сериализации, включая Thrift, Kryo, Avro, и благодаря отличным результатам по скорости обработки, размеру данных и адаптации к изменениям схем данных, остановилась на Protobuf. Использование Protobuf обеспечило существенное сокращение времени сериализации и десериализации, положительно повлияло на производительность всего сервиса. Изначально планировалось внедрять Protobuf вместе с протоколом gRPC, который хорошо совместим с Protobuf и эффективен для межсервисного взаимодействия. Однако в процессе выяснилось, что gRPC не поддерживается балансировщиками нагрузки AWS Application Load Balancers, что требует значительных изменений инфраструктуры.
Поэтому на первом этапе выполнен перевод именно на Protobuf поверх существующего REST API, а полное переход к gRPC отложен на будущее. Миграция с JSON на Protobuf — это несовместимое изменение, поэтому команда Atlassian разработала специальный многоэтапный процесс, позволяющий выполнять миграцию без прерывания работы пользователей и с возможностью контроля корректности данных. Для этого были внедрены параллельные эндпоинты по возврату данных в Protobuf-формате как в монолите, так и в Issue Service, реализована система флагов, позволяющая переключаться между старой и новой логикой, а также сравнивать результаты обоих форматов для выявления различий. Такой подход позволил последовательно фиксировать и исправлять ошибки в новом коде, а после успешного тестирования — полностью отказаться от JSON. Внедрение Protobuf привело к значительным положительным изменениям.
Расход CPU на кластере Memcached снизился примерно на 75%, что свидетельствует о более эффективном использовании вычислительных ресурсов. Размер сериализованных данных сократился почти на 80%, что позволило существенно уменьшить объем передаваемой и хранимой информации, а значит - сэкономить память и повысить скорость передачи. При измерении времени обработки данных наблюдались ускорения: по 99-му перцентилю сериализация и десериализация выполнялись в 4 и 33 раза быстрее соответственно, а серверная обработка запроса — на 20% быстрее в сравнении с JSON. Сокращение размера данных и повышение скорости обработки позволили снизить требования к инфраструктуре Memcached, уменьшив количество необходимых узлов кластера на 55%. При этом команда столкнулась с определенными особенностями Protobuf, которые требовалось учитывать.
В отличие от JSON, Protobuf не поддерживает значения NULL: поля либо отсутствуют, либо имеют значение по умолчанию (например, пустую строку или 0). Это осложняет различение фактического отсутствия значения и значения по умолчанию. Решение было найдено в явном пропуске данных по полю, если оно не задано, а на этапе чтения данных при необходимости добавляется пустое значение. Эта оптимизация значительно уменьшила объем хранимых данных, несмотря на небольшие дополнительные вычисления при чтении. Также возникали трудности интеграции Protobuf с фреймворком Spring, широко используемым в микросервисах Atlassian.
Стандартный обработчик ошибок Spring, который ожидал JSON-ответы, приводил к исключениям при попытке обработать Protobuf-формат. Чтобы сохранить корректные коды ошибок, была разработана своя обработка исключений, позволяющая возвращать исходные коды HTTP. Еще одним нюансом была особенность передачи пустых Protobuf-сообщений — в отличие от JSON, где пустой объект передается как «{}», Protobuf передает их как пустой массив байтов, который иногда интерпретируется как NULL. Для предотвращения проблем с разбором данных применялось специальное условие, подставляющее пустой массив при получении NULL. Во время первичного запуска нового формата в Memcached возникла высокая частота вытеснения данных из кэша, что негативно сказалось на скорости отклика сервиса.
Это оказалось следствием особенностей механизма распределения памяти Memcached — выделения страниц и сластов (slabs) по размеру объектов, которые долгое время были настроены под JSON и не подходили для меньших Protobuf-объектов. Для решения проблемы на кластерах пришлось запустить постепенный перезапуск узлов для перераспределения памяти, а также команда дождалась автоматического перераспределения страниц в новых версиях Memcached с включенной функцией slab_automove. После этого кэш-хиты вернулись на прежний уровень, что заметно улучшило стабильность и производительность. В итоге переход на Protobuf в Jira Cloud продемонстрировал значительную пользу: заметное повышение скорости обработки данных, сокращение нагрузки на инфраструктуру, уменьшение размера кэшируемых данных и рост масштабируемости системы. Несмотря на возникшие технические сложности и необходимость решения вопросов совместимости и обработки ошибок, результат оправдал вложенные усилия и стал основой для дальнейшего роста и улучшения платформы.
Этот опыт иллюстрирует важность выбора эффективных форматов сериализации в сложных облачных системах и подчеркивает роль постоянного совершенствования архитектуры в обеспечении высокого качества сервиса и удобства для пользователей. Вместе с продолжающейся эволюцией микросервисной архитектуры, Jira Cloud с применением Protobuf получает мощный инструмент для устойчивого развития и поддержки миллионов пользователей по всему миру.