Привет! Я Даниэль Халиулин, технический менеджер продукта в Тинькофф. Отвечаю за надежность и производительность нашего основного приложения — мобильного банка. Руковожу двумя одноименными командами, которые занимаются производительностью и надежностью. Расскажу про направления SRE и Observability в мобильных приложениях.

Как однажды все упало

А теперь сделаем ВЖУХ и переместимся в 2020 год. Одним прекрасным пятничным вечером в чат по сбоям пришло сообщение: 

Чуть зацензурированное, но посыл передает
Чуть зацензурированное, но посыл передает

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

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

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

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

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

Изменения быстро откатили, конфиг снова стал рабочим. Казалось бы, проблема решена, но вот сюрприз — невалидный конфиг кэшируется в мобильном приложении на 24 часа. Это значит, что все, кто зашли в приложение, пока конфиг был нерабочий, закэшировали себе на телефоне невалидную версию. И приложение в течение 24 часов не пойдет за новой версией. У клиентов было два варианта: не пользоваться приложением сутки или переустановить приложение. И то и другое не тот уровень сервиса, к которому мы стремимся.

Возможно, вы читаете это сейчас и думаете: какой бизнес-аналитик?! Какие изменения в пятницу вечером на проде?! Где хоть какая-нибудь валидация или ревью?! Именно эти вопросы я мысленно задавал, когда был на том созвоне. Мысленно — потому что это была буквально вторая неделя моей работы в Тинькофф.

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

Справедливости ради нужно сделать две оговорки:

  1. На iOS все работало, проблема была только на Android. Но на Android у нас большинство клиентов, так что это не особо нас спасало.

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

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

Серверная инфраструктура работала в этот момент прекрасно. Все было отлично — SRE-практики, best practices выполнялись. Но клиенты не могли пользоваться нашим сервисом именно из-за мобильного приложения.

Дальше расскажу вот о чем:

  • Чем SRE в мобилках отличается от SRE на бэкендах. Какие есть вещи в мобилках, которые не снились чувакам с бэкендов.

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

  • Про observability мобильного банка и про то, какие данные мы собираем.

Что classic SRE даже не снилось

Работа Mobile SRE имеет ряд важных отличий по сравнению с классическим SRE. Вот список отличий, которые мы заметили.

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

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

Зависим от пары монополистов — Apple и Google. В мобилках мы зависим от их требований к тому, как должны работать приложения, и в силу известных обстоятельств ситуация немного меняется. Но anyway есть сторы, которые занимаются распространением приложений, и у них есть требования, которые мы вынуждены выполнять, хотим того или нет.

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

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

Какие-то проблемы обязательно моделируются только на Samsung, с определенной версией оболочки и определенной версией WebView. Каждый вендор стремится навернуть что-то свое — программные оболочки или особенности API.

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

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

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

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

Когда я говорю про хвост версий, имею в виду два момента: 

  • Необходимость поддерживать старые контракты API, чтобы у людей на телефонах работали старые версии. У нас есть правило: если поле или API зарелизилось, то даже когда мы избавились от него, ожидаем, что на серверной стороне оно будет поддерживаться еще как минимум два года. Мы обеспечиваем обратную совместимость и не ломаем клиентам старые версии приложения.

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

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

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

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

Mobile SRE в нашем банке

Сейчас над мобильным банком работают примерно 150 разработчиков на каждой платформе — Android и iOS. Огромное количество людей пишет код денно и нощно, бизнес бежит, фичи появляются все быстрее и быстрее. В таком ритме легко влить код, который будет тормозить приложение. Даже если по чуть-чуть, приложение разрастается в тормозящего монстра, с которым неприятно работать.

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

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

Список задач команды выглядит так: 

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

  • Оптимизировать время запуска и открытия важных экранов.

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

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

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

Список задач команды выглядит так: 

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

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

  • Разработка общих инструментов и гайдов для диагностики инцидентов.

  • Архитектурные оптимизации, направленные на улучшение надежности.

Еще пара инсайтов про работу команд производительности и надежности.

Оказалось, что командам нужны аналитики и тестировщики даже в такой технический домен. У нас в компании много команд, и из-за этого возникает много коммуникации — с кем-то договориться, кого-то убедить. Нельзя забывать про необходимость грамотного ТЗ.

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

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

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

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

Особенности observability в мобильных приложениях

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

В мобильных есть пара баянистых метрик, о которых просто нельзя умолчать, затрагивая эту тему: 

  1. Crashrate — то, как часто приложение падает у клиента. У метрики есть много особенностей: например, приложение может падать из-за проблем в операционной системе или в фоне, когда клиент им не пользуется.

    Такие краши нельзя игнорировать, потому что следующий запуск приложения будет «на холодную», а он всегда дольше. Если следить за крашами в Google Console, довольно показательна метрика User Perceived Crash rate — она показывает, какой процент крашей реально повлиял на взаимодействие пользователя с приложением.

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

Для сбора этих данных на Android мы используем Firebase и AppCenter. На iOS — только AppCenter
Для сбора этих данных на Android мы используем Firebase и AppCenter. На iOS — только AppCenter

Прежде чем рассказать о следующей проверке, напомню контекст.

20 миллионов клиентов заходят в наше приложение каждый месяц. А каждый день это делают 10 миллионов клиентов. Над приложением работает порядка 60 фиче-команд, у которых свои бэклоги, бизнесы и все такое. Такой масштаб привносит коррективы, и если какая-то команда решит добавить хотя бы один HTTP-запрос на старт приложения, можно представить, какую нагрузку это принесет в нашу инфраструктуру.

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

Мы реализовали автотест, который раз в ночь запускается на CI. Суть этого теста — запустить приложение, авторизоваться, дождаться загрузки главного экрана и затрекать состав запросов. Этот же автотест сверяет фактический состав запросов с нашим эталоном. Если все сошлось — живем дальше. Если что-то не сошлось, команда производительности получает алерт прямо в ухо.

Примерно так выглядит вся схема
Примерно так выглядит вся схема

Алерт выглядит так: 

В алерт входит название теста и список запросов, которые добавились
В алерт входит название теста и список запросов, которые добавились

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

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

Время показа экранов делится на два сценария:

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

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

  1. Метрики «плашек». У нас есть сборные экраны, они состоят из элементов, за которые отвечают отдельные команды. Это не значит, что они отвечают только за этот элемент на экране, просто ответственность за весь экран размазывается на несколько команд. На таких экранах мы трекаем время загрузки и отображения каждого элемента, чтобы понимать его вклад в общее время и отслеживать тренд релиз к релизу.

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

Зачем запускаем приложения сотни раз


Перформанс-тест — когда мы запускаем приложение сотни раз определенным образом, обрабатываем перформанс-метрики и делаем выводы о том, стало ли приложение работать быстрее или медленнее по сравнению с эталоном.

Схема работы перформанс-тестов в мобильном приложении
Схема работы перформанс-тестов в мобильном приложении

Сначала для честности тестов нужно замокать сеть. Если сеть не замокать, результаты прогонов будут флапать туда-сюда по 100—500 миллисекунд и никакой стабильности метрик не будет.

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

Один прогон выполняется за одно время, второй — на 30% дольше, третий — на 40% и так далее. Мы даже попробовали запускать перформанс-тесты на реальных устройствах, но стало еще хуже. Из-за большого количества запусков девайс нагревался, операционная система троттлила ресурсы — и плакали наши метрики и стабильность прогонов.

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

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

И здесь мы видим, что в сравнении с эталоном эта метрика ускорилась на 44%
И здесь мы видим, что в сравнении с эталоном эта метрика ускорилась на 44%

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

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

Почему храним логи у клиентов

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

В мобильном банке есть два типа логов: онлайн-логи и on-demand-логи.

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

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

Sage — observability-платформа, которая появилась в качестве замены Splunk, когда он ушел из России. Платформа позволяет писать, обрабатывать и визуализировать логи и метрики, создавать сложные алерты над этими данными. Система адаптирована под большую нагрузку — успешно вывозит весь объем логов и метрик Тинькофф

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

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

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

Схема записи логов в системе. МБ — мобильный банк
Схема записи логов в системе. МБ — мобильный банк

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

Несколько идей на прощание

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

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

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

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

С мобильных приложений можно собирать много полезных метрик. Я рассказал только о части метрик, которые показались полезными нам.

Комментарии (14)


  1. BigD
    19.09.2023 16:11

    А чем сейчас занимается ваша iOS команда?


    1. khaliulin Автор
      19.09.2023 16:11

      Часть продолжает пилить фичи, чтобы клиенты получили их, когда мы релизнимся в App Store. Другая часть активно работает над тем, что бы этот самый релиз случился:)


      1. pawellrus
        19.09.2023 16:11

        когда мы релизнимся в App Store

        А вы оптимисты.


        1. khaliulin Автор
          19.09.2023 16:11

          Не без это, конечно.

          Но факты говорят сами за себя - некоторым банкам, которые тоже удалили из App Store, удалось опубликоваться с разной степенью камуфляжа. Так что это не что-то невозможное.


          1. pawellrus
            19.09.2023 16:11

            Жизнь от удаления до удаления - такое себе. Одна новость в интернете и приложения снесут, и нужно готовить инфраструктуру для публикации заново, снова доносить информацию клиентам о новом названии приложения, не привлекая внимания санитаров модераторов.

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


            1. khaliulin Автор
              19.09.2023 16:11

              В целом, согласен.

              А какие альтернативы вы видите? При условии, что клиенты с iOS никуда не ушли и их все равно значимая часть.


              1. pawellrus
                19.09.2023 16:11
                +1

                Научить банкоматы устанавливать приложение на подключенный айфон разве что. :)

                Остальные варианты слишком субъективные, холиварные или провокационные чтобы их озвучивать.


  1. Rusrst
    19.09.2023 16:11

    Спасибо, было интересно! В Тинькофф собралась грамотная команда.

    Я так понимаю, что vitals для вас больше не актуален, в связи с исключением из стора?


    1. khaliulin Автор
      19.09.2023 16:11

      Спасибо за обратную связь!

      Да, с Vitals все так. А другим сторам ещё далеко до такой аналитики, которую давал Google.


  1. ozzyBLR
    19.09.2023 16:11
    +1

    Доступно и познавательно, большое спасибо. Огромную работу проделывают команды - и ничуть не меньшую проделал автор статьи, составляя её.


    1. khaliulin Автор
      19.09.2023 16:11

      Спасибо за добрую обратную связь!


  1. ws233
    19.09.2023 16:11
    +1

    Спасибо за статью. Качественно и полезно.

    Ждем "отдельный доклад или статью" о том, как фильтровать и группировать результаты производительности запуска экранов. Без них данная статья выглядит неполной.


  1. BigD
    19.09.2023 16:11

    А как именно iOS приложение ставят в банках? Технически. Sideloading?


    1. khaliulin Автор
      19.09.2023 16:11

      Технически это восстановление приложения с другого аккаунта, на котором нужное приложение есть. Такое умеет делать iMazing и такую логику можно воспроизвести с использованием open source утилит